Uniapp 中 uni.request 的二次封装
Uniapp 中 uni.request 的二次封装
在 Uniapp 项目中,对 uni.request
进行二次封装可以提高代码复用性、统一处理错误和简化 API 调用。下面是一个完整的封装方案:
一、基础封装
1. 创建 request.js 工具文件
// utils/request.js// 基础配置
const config = {baseURL: process.env.VUE_APP_API_BASE_URL || '', // 从环境变量获取基础URLtimeout: 10000, // 请求超时时间header: {'Content-Type': 'application/json'}
};// 请求拦截器
const requestInterceptor = (options) => {// 添加token等全局headerconst token = uni.getStorageSync('token');if (token) {options.header = options.header || {};options.header['Authorization'] = `Bearer ${token}`;}// 可以在这里处理其他全局逻辑return options;
};// 响应拦截器
const responseInterceptor = (response, resolve, reject) => {const { statusCode, data } = response;if (statusCode === 200) {// 这里根据后端接口规范调整判断逻辑if (data.code === 0 || data.success) {resolve(data.data || data);} else {// 业务逻辑错误uni.showToast({title: data.message || '请求失败',icon: 'none'});reject(data);}} else {// HTTP错误handleHttpError(statusCode, reject);}
};// 处理HTTP错误
const handleHttpError = (statusCode, reject) => {let errMessage = '请求失败';switch (statusCode) {case 401:errMessage = '未授权,请登录';// 可以跳转到登录页break;case 403:errMessage = '拒绝访问';break;case 404:errMessage = '请求地址错误';break;case 500:errMessage = '服务器内部错误';break;default:errMessage = `网络错误 (${statusCode})`;}uni.showToast({title: errMessage,icon: 'none'});reject({ statusCode, message: errMessage });
};// 核心请求方法
const request = (options) => {return new Promise((resolve, reject) => {// 合并配置options = {...config,...options,url: config.baseURL + options.url,header: {...config.header,...(options.header || {})}};// 请求拦截options = requestInterceptor(options) || options;// 发起请求uni.request({...options,success: (res) => {responseInterceptor(res, resolve, reject);},fail: (err) => {uni.showToast({title: '网络连接失败',icon: 'none'});reject(err);},complete: () => {// 可以在这里处理loading状态等}});});
};// 导出常用方法
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});},// 原始request方法request
};
二、高级功能扩展
1. 添加取消请求功能
// 在request.js中添加const pendingRequests = new Map();// 生成请求唯一标识
const generateReqKey = (config) => {const { url, method, params = {}, data = {} } = config;return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&');
};// 添加到请求拦截器
const requestInterceptor = (options) => {// ...其他拦截逻辑const reqKey = generateReqKey(options);if (pendingRequests.has(reqKey)) {const cancel = pendingRequests.get(reqKey);cancel('取消重复请求');pendingRequests.delete(reqKey);}options.cancelToken = options.cancelToken || new CancelToken((cancel) => {pendingRequests.set(reqKey, cancel);});return options;
};// 添加到响应拦截器
const responseInterceptor = (response, resolve, reject) => {const reqKey = generateReqKey(response.config);pendingRequests.delete(reqKey);// ...其他逻辑
};// CancelToken构造函数
function CancelToken(executor) {let cancel;this.promise = new Promise((resolve) => {cancel = resolve;});executor(cancel);
}// 导出取消请求方法
export const cancelRequest = (config) => {const reqKey = generateReqKey(config);if (pendingRequests.has(reqKey)) {const cancel = pendingRequests.get(reqKey);cancel('手动取消请求');pendingRequests.delete(reqKey);}
};
2. 添加请求重试机制
// 修改request方法
const request = (options, retryCount = 0) => {return new Promise((resolve, reject) => {// ...原有逻辑uni.request({// ...原有配置fail: (err) => {if (retryCount < (options.retry || 3)) {setTimeout(() => {request(options, retryCount + 1).then(resolve).catch(reject);}, 1000 * (retryCount + 1));} else {uni.showToast({title: '网络连接失败',icon: 'none'});reject(err);}}});});
};
三、使用示例
1. 基本使用
import http from '@/utils/request';// GET请求
http.get('/api/user', { id: 123 }).then(data => {console.log(data);}).catch(err => {console.error(err);});// POST请求
http.post('/api/login', { username: 'admin', password: '123456' }).then(data => {console.log(data);});
2. 带额外配置的请求
http.post('/api/upload', formData, {header: {'Content-Type': 'multipart/form-data'},timeout: 30000 // 单独设置超时时间
});
3. 取消请求
const requestTask = http.get('/api/large-data');// 需要取消时
requestTask.cancel(); // 如果实现了取消功能
四、在 Vue 中全局挂载
1. 在 main.js 中挂载
// main.js
import Vue from 'vue';
import http from '@/utils/request';Vue.prototype.$http = http;
2. 在组件中使用
// 组件内
export default {methods: {fetchData() {this.$http.get('/api/data').then(data => {// 处理数据});}}
}
五、TypeScript 支持
如果你使用 TypeScript,可以添加类型定义:
// types/request.d.ts
declare module '@/utils/request' {interface RequestConfig {url: string;method?: 'GET' | 'POST' | 'PUT' | 'DELETE';data?: any;params?: any;header?: Record<string, string>;timeout?: number;[key: string]: any;}interface ResponseData<T = any> {code: number;message: string;data: T;}interface RequestInstance {request<T = any>(config: RequestConfig): Promise<T>;get<T = any>(url: string, params?: any, config?: RequestConfig): Promise<T>;post<T = any>(url: string, data?: any, config?: RequestConfig): Promise<T>;put<T = any>(url: string, data?: any, config?: RequestConfig): Promise<T>;delete<T = any>(url: string, data?: any, config?: RequestConfig): Promise<T>;}const http: RequestInstance;export default http;
}
六、最佳实践建议
- 环境变量管理:将 baseURL 等配置通过环境变量管理
- 错误处理:根据业务需求统一处理错误码
- Loading状态:可以集成全局Loading状态控制
- Mock数据:开发环境下可以集成Mock功能
- 缓存策略:对某些GET请求可以添加缓存功能
- 性能监控:可以添加请求耗时统计等功能
- 日志记录:重要请求可以添加日志记录功能
这样的封装可以大大提升项目的开发效率和可维护性,同时保持足够的灵活性应对各种业务场景。