Skip to content

網路請求

在 uni-app 中,網路請求是應用開發中非常重要的一部分。本文將詳細介紹如何在 uni-app 中進行網路請求,包括基本用法、進階技巧以及最佳實踐。

uni.request 基礎用法

uni-app 提供了統一的網路請求 API uni.request,可以在各個平台上進行網路請求操作。

基本語法

js
uni.request({
  url: 'https://api.example.com/data', // 請求的接口地址
  method: 'GET', // 請求方法,可選:GET、POST、PUT、DELETE 等
  data: {}, // 請求的參數
  header: {}, // 設定請求的 header
  success: (res) => {}, // 請求成功後的回調函數
  fail: (err) => {}, // 請求失敗後的回調函數
  complete: () => {} // 請求完成後的回調函數(無論成功與否)
})

GET 請求示例

js
uni.request({
  url: 'https://api.example.com/users',
  method: 'GET',
  data: {
    page: 1,
    limit: 10
  },
  header: {
    'content-type': 'application/json'
  },
  success: (res) => {
    console.log('請求成功:', res.data);
  },
  fail: (err) => {
    console.error('請求失敗:', err);
  }
})

POST 請求示例

js
uni.request({
  url: 'https://api.example.com/users',
  method: 'POST',
  data: {
    username: 'test',
    password: '123456'
  },
  header: {
    'content-type': 'application/json'
  },
  success: (res) => {
    console.log('請求成功:', res.data);
  },
  fail: (err) => {
    console.error('請求失敗:', err);
  }
})

Promise 風格的網路請求

uni-app 支援使用 Promise 風格的 API,可以讓程式碼更加簡潔。

js
// Promise 方式調用
uni.request({
  url: 'https://api.example.com/users',
  method: 'GET'
})
.then(res => {
  console.log('請求成功:', res[1].data);
})
.catch(err => {
  console.error('請求失敗:', err);
})

封裝網路請求

在實際專案中,我們通常會對網路請求進行封裝,以便統一處理請求和響應。

js
// request.js
const baseURL = 'https://api.example.com';

// 請求攔截器
const beforeRequest = (config) => {
  // 添加 token
  const token = uni.getStorageSync('token');
  if (token) {
    config.header = {
      ...config.header,
      'Authorization': `Bearer ${token}`
    };
  }
  return config;
};

// 響應攔截器
const handleResponse = (res) => {
  // 請求成功
  if (res.statusCode >= 200 && res.statusCode < 300) {
    return res.data;
  }
  
  // 未授權
  if (res.statusCode === 401) {
    uni.showToast({
      title: '登入已過期,請重新登入',
      icon: 'none'
    });
    // 跳轉到登入頁
    setTimeout(() => {
      uni.navigateTo({
        url: '/pages/login/login'
      });
    }, 1500);
    return Promise.reject(new Error('未授權'));
  }
  
  // 其他錯誤
  uni.showToast({
    title: res.data.message || '請求失敗',
    icon: 'none'
  });
  return Promise.reject(res);
};

// 封裝請求方法
const request = (options) => {
  const config = beforeRequest({
    ...options,
    url: options.url.startsWith('http') ? options.url : baseURL + options.url,
    header: options.header || {
      'content-type': 'application/json'
    }
  });
  
  return new Promise((resolve, reject) => {
    uni.request({
      ...config,
      success: (res) => {
        try {
          const data = handleResponse(res);
          resolve(data);
        } catch (error) {
          reject(error);
        }
      },
      fail: (err) => {
        uni.showToast({
          title: '網路異常,請檢查網路連接',
          icon: 'none'
        });
        reject(err);
      }
    });
  });
};

// 導出請求方法
export default {
  get: (url, data, options = {}) => {
    return request({
      url,
      data,
      method: 'GET',
      ...options
    });
  },
  post: (url, data, options = {}) => {
    return request({
      url,
      data,
      method: 'POST',
      ...options
    });
  },
  put: (url, data, options = {}) => {
    return request({
      url,
      data,
      method: 'PUT',
      ...options
    });
  },
  delete: (url, data, options = {}) => {
    return request({
      url,
      data,
      method: 'DELETE',
      ...options
    });
  }
};

使用封裝後的請求方法:

js
import request from '@/utils/request';

// GET 請求
request.get('/users', { page: 1, limit: 10 })
  .then(res => {
    console.log('獲取用戶列表成功:', res);
  })
  .catch(err => {
    console.error('獲取用戶列表失敗:', err);
  });

// POST 請求
request.post('/users', { username: 'test', password: '123456' })
  .then(res => {
    console.log('創建用戶成功:', res);
  })
  .catch(err => {
    console.error('創建用戶失敗:', err);
  });

檔案上傳

uni-app 提供了 uni.uploadFile 方法用於檔案上傳。

js
uni.uploadFile({
  url: 'https://api.example.com/upload',
  filePath: tempFilePath,
  name: 'file',
  formData: {
    'user': 'test'
  },
  success: (res) => {
    console.log('上傳成功:', res.data);
  },
  fail: (err) => {
    console.error('上傳失敗:', err);
  }
});

檔案下載

uni-app 提供了 uni.downloadFile 方法用於檔案下載。

js
uni.downloadFile({
  url: 'https://example.com/somefile.pdf',
  success: (res) => {
    if (res.statusCode === 200) {
      console.log('下載成功:', res.tempFilePath);
      // 儲存檔案
      uni.saveFile({
        tempFilePath: res.tempFilePath,
        success: (saveRes) => {
          console.log('檔案儲存成功:', saveRes.savedFilePath);
        }
      });
    }
  },
  fail: (err) => {
    console.error('下載失敗:', err);
  }
});

網路狀態監聽

uni-app 提供了網路狀態監聽的 API,可以即時獲取網路狀態變化。

js
// 獲取當前網路狀態
uni.getNetworkType({
  success: (res) => {
    console.log('當前網路類型:', res.networkType);
  }
});

// 監聽網路狀態變化
uni.onNetworkStatusChange((res) => {
  console.log('網路類型:', res.networkType);
  console.log('是否有網路連接:', res.isConnected);
});

WebSocket 支援

uni-app 提供了 WebSocket 相關的 API,可以實現即時通訊。

js
// 創建 WebSocket 連接
const socketTask = uni.connectSocket({
  url: 'wss://example.com/socket',
  header: {
    'content-type': 'application/json'
  },
  success: () => {
    console.log('WebSocket 連接成功');
  }
});

// 監聽 WebSocket 打開
socketTask.onOpen(() => {
  console.log('WebSocket 已打開');
  // 發送訊息
  socketTask.send({
    data: JSON.stringify({ type: 'login', data: { userId: '123' } }),
    success: () => {
      console.log('訊息發送成功');
    }
  });
});

// 監聽 WebSocket 接收訊息
socketTask.onMessage((res) => {
  console.log('收到伺服器訊息:', res.data);
});

// 監聽 WebSocket 錯誤
socketTask.onError((res) => {
  console.error('WebSocket 錯誤:', res);
});

// 監聽 WebSocket 關閉
socketTask.onClose(() => {
  console.log('WebSocket 已關閉');
});

// 關閉 WebSocket 連接
// socketTask.close();

最佳實踐

1. 統一錯誤處理

在網路請求封裝中,統一處理錯誤訊息,避免在每個請求中重複處理。

2. 請求取消

在頁面銷毀時,取消未完成的請求,避免記憶體洩漏。

js
let requestTask = null;

// 發起請求
requestTask = uni.request({
  url: 'https://api.example.com/data',
  // ...其他配置
});

// 在頁面 onUnload 生命週期中取消請求
onUnload() {
  if (requestTask) {
    requestTask.abort();
  }
}

3. 請求重試

對於重要的請求,可以實現請求重試機制。

js
const requestWithRetry = (options, maxRetries = 3) => {
  return new Promise((resolve, reject) => {
    const attempt = (retryCount) => {
      request(options)
        .then(resolve)
        .catch(error => {
          if (retryCount < maxRetries) {
            console.log(`請求失敗,第 ${retryCount + 1} 次重試`);
            setTimeout(() => {
              attempt(retryCount + 1);
            }, 1000 * Math.pow(2, retryCount)); // 指數退避策略
          } else {
            reject(error);
          }
        });
    };
    attempt(0);
  });
};

4. 資料快取

對於不經常變化的資料,可以實現本地快取,減少網路請求。

js
const cachedRequest = (url, expireTime = 60 * 1000) => {
  const cacheKey = `cache_${url}`;
  const cacheData = uni.getStorageSync(cacheKey);
  
  if (cacheData && cacheData.timestamp) {
    const now = new Date().getTime();
    if (now - cacheData.timestamp < expireTime) {
      return Promise.resolve(cacheData.data);
    }
  }
  
  return request.get(url).then(res => {
    uni.setStorageSync(cacheKey, {
      data: res,
      timestamp: new Date().getTime()
    });
    return res;
  });
};

5. 接口模組化

將接口按照業務模組進行分類,便於管理和維護。

js
// api/user.js
import request from '@/utils/request';

export default {
  login: (data) => request.post('/user/login', data),
  register: (data) => request.post('/user/register', data),
  getUserInfo: () => request.get('/user/info'),
  updateUserInfo: (data) => request.put('/user/info', data)
};

// api/product.js
import request from '@/utils/request';

export default {
  getProductList: (params) => request.get('/products', params),
  getProductDetail: (id) => request.get(`/products/${id}`),
  createProduct: (data) => request.post('/products', data),
  updateProduct: (id, data) => request.put(`/products/${id}`, data),
  deleteProduct: (id) => request.delete(`/products/${id}`)
};

// 使用
import userApi from '@/api/user';
import productApi from '@/api/product';

// 登入
userApi.login({ username: 'test', password: '123456' })
  .then(res => {
    console.log('登入成功:', res);
  });

// 獲取產品列表
productApi.getProductList({ page: 1, limit: 10 })
  .then(res => {
    console.log('產品列表:', res);
  });

跨域處理

在開發環境中,可能會遇到跨域問題。uni-app 提供了代理配置來解決跨域問題。

在專案根目錄下創建 vue.config.js 檔案:

js
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'https://api.example.com',
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  }
};

小程式特殊處理

在小程式中,網路請求需要配置合法域名,否則會被攔截。

  1. 在小程式管理後台的"開發設定"中添加伺服器域名
  2. 在開發環境中,可以在開發工具中勾選"不校驗合法域名"選項

總結

本文介紹了 uni-app 中網路請求的基本用法、進階技巧以及最佳實踐。通過合理封裝和使用網路請求,可以提高程式碼的可維護性和可複用性,同時也能提升應用的用戶體驗。在實際開發中,應根據專案需求選擇合適的網路請求方式和策略。

一次開發,多端部署 - 讓跨平台開發更簡單