uni-app 请求封装
一、封装目的
在 uni-app 项目中,接口请求频繁且业务逻辑复杂。
如果每次都直接调用 uni.request,会遇到以下问题:
❌ 每个页面都要手动配置 BaseURL、Header 等公共参数;
❌ 不能统一处理登录过期、token 校验等逻辑;
❌ 无法优雅地控制 loading 状态(并发、闪烁问题);
❌ 错误提示风格不一致;
❌ 重复代码过多,维护成本高。
为了提升开发体验与可维护性,我们对 uni.request 进行二次封装,
实现以下功能:
| 功能项 | 说明 |
|---|---|
| 全局默认配置 | 统一 BaseURL、超时时间、请求头 |
| 请求/响应拦截器 | 统一 token 注入与响应处理 |
| 登录过期处理 | 自动跳转登录页、清除缓存 |
| loading 控制 | 并发请求只显示一个 loading,避免闪烁 |
| 自定义实例 | 可快速创建带不同配置的请求实例 |
| GET/POST 快捷方法 | 简化调用形式 |
二、代码结构概览
文件结构建议如下:
/utils├─ env.js // 环境配置(开发/生产)├─ request.js // 本封装的核心文件
env.js 示例
export default {baseURL: 'http://123.56.125.118:8300'
};
三、核心实现逻辑
1️⃣ 默认配置管理
defaultProps = {BaseURL: env.baseURL,timeout: 10000,header: {},isLoading: true, // 是否显示loading
};
BaseURL:项目全局接口地址,统一管理。
timeout:请求超时时间(毫秒)。
header:默认请求头,可在实例化时追加。
isLoading:是否在该请求中显示加载提示,可在单个请求中控制。
2️⃣ 请求与响应拦截器
请求拦截(request)
在每次发起请求前自动携带 token。
request: (options) => {let token = uni.getStorageSync('token');if (token) options.header['token'] = token;return options;
}
📘 意义:
无需在每个请求中手动写入 token,实现统一认证。
响应拦截(response)
对后端返回的数据进行集中处理。
response: (res) => {if (res.isSuccess) {let code = res.data.code;switch (code) {case 200:return res.data;case 208:case 401:return this.timeoutHandler(res.data);default:uni.showToast({title: '程序出现错误',icon: 'none'});}} else {uni.showToast({title: '请求失败',icon: 'none'});return Promise.reject(res);}
}
📘 意义:
统一处理后端业务状态码,例如:
200:成功返回;401 / 208:登录过期,触发跳转;其他状态:友好提示。
3️⃣ Loading 并发控制与防闪烁
核心机制:
// 当前进行中的请求数量
loadingCount = 0;// 防止连续请求时闪烁
timer = null;
逻辑说明
| 场景 | 行为 |
|---|---|
| 第一个请求开始 | 显示 Loading |
| 并发多个请求 | loadingCount 递增,不重复显示 |
| 每个请求完成 | loadingCount 递减 |
| 所有请求结束 | 延迟 400ms 后隐藏 Loading(防止闪烁) |
实现代码
if (requestOptions.isLoading) {if (this.timer) {clearTimeout(this.timer);this.timer = null;}this.loadingCount++;if (this.loadingCount === 1) {uni.showLoading({ title: '加载中', mask: true });}
}
并在请求的 complete 回调中计数递减:
complete: () => {if (requestOptions.isLoading) {this.loadingCount = Math.max(this.loadingCount - 1, 0);if (this.loadingCount === 0) {this.timer = setTimeout(() => {uni.hideLoading();this.timer = null;}, 400);}}
}
📘 意义:
解决了「并发多次 loading」与「连续请求闪烁」两大问题,
提升交互流畅度。
4️⃣ 登录过期自动处理
timeoutHandler(res) {uni.clearStorageSync();uni.navigateTo({ url: '/pages/login/index' });uni.showToast({title: '登录过期,请重新登录',icon: 'none',});return res;
}
📘 意义:
后端返回 401/208 时,自动清空缓存并跳转登录页,
无需在业务层单独判断。
5️⃣ 提供简洁调用方法
get(options) {return this.request({ ...options, method: 'GET' });
}post(options) {return this.request({ ...options, method: 'POST' });
}
📘 意义:
简化业务层调用,如:
WxRequest.get({ url: '/user/info' });
WxRequest.post({ url: '/login', data: form });
6️⃣ 自定义实例化
const customInstance = (options) => {return new WxRequestClass(options);
};
📘 意义:
当你需要访问不同后端服务或配置(如文件上传、mock 服务)时:
const uploadRequest = customInstance({BaseURL: 'http://upload.api.com',header: { 'Content-Type': 'multipart/form-data' }
});
四、使用示例
1️⃣ 基础 GET 请求
WxRequest.get({url: '/user/profile',
}).then(res => {console.log('用户资料', res);
});
2️⃣ POST 请求(带参数)
WxRequest.post({url: '/login',data: {username: 'admin',password: '123456'}
});
3️⃣ 禁止 loading 的请求
WxRequest.get({url: '/home/banner',isLoading: false // 不显示 loading
});
4️⃣ 并发请求示例
WxRequest.all(WxRequest.get({ url: '/user/info' }),WxRequest.get({ url: '/user/setting' })
).then(([info, setting]) => {console.log(info, setting);
});
📘 效果:
两个请求同时发起,只显示一个“加载中”,
等两个请求都完成后再隐藏。
完整代码
import env from './env.js';class WxRequestClass {// 存放一些默认配置:根地址、超时时间、请求头,这些属于全局配置一般情况下是不会改变的defaultProps = {BaseURL: env.baseURL,timeout: 10000,header: {},isLoading: true, // 是否显示loading效果};// 用来解决高并发请求时,从开始请求显示loading,到全部完成隐藏loadingloadingCount = 0;// 解决连续请求时,看到的闪烁loading问题 (相当于防抖)timer = null;// 配置请求、响应拦截器interceptor = {request: (options) => {let token = uni.getStorageSync('token');if (token) {options.header['token'] = token;}return options;},response: (res) => {if (res.isSuccess) {let code = res.data.code;switch (code) {case 200:return res.data;case 208:return this.timeoutHandler(res.data);case 401:return this.timeoutHandler(res.data);default:// return uni.showToast({// title: '程序出现错误,正在修复',// icon: 'none',// duration: 2000,// });}} else {uni.showToast({title: '请求失败',icon: 'none',duration: 2000,});return Promise.reject(res);}},};constructor(defaultProps1) {if (defaultProps1) {this.defaultProps = Object.assign({}, this.defaultProps, defaultProps1);}}request(options) {let requestOptions = Object.assign({}, this.defaultProps, options);requestOptions.url = requestOptions.BaseURL + (options.url.startsWith('/') ? options.url : '/' + options.url);let interceptorRequest = this.interceptor.request(requestOptions);if (requestOptions.isLoading) {if (this.timer) {clearTimeout(this.timer);this.timer = null;}this.loadingCount++;console.log(this.loadingCount, 'this.loadingCount当前数量');if (this.loadingCount === 1) {uni.showLoading({title: '加载中',mask: true,});}}return new Promise(async (resolve, reject) => {try {uni.request({...interceptorRequest,success: (res) => {let interceptorResponse = this.interceptor.response({...res,config: interceptorRequest,isSuccess: true});resolve(interceptorResponse);},fail: (err) => {let interceptorResponse = this.interceptor.response({...err,config: interceptorRequest,isSuccess: false});reject(interceptorResponse);},complete: () => {if (requestOptions.isLoading) {this.loadingCount--;if (this.loadingCount === 0) {this.timer = setTimeout(() => {uni.hideLoading();this.timer = null;}, 400);}}},});} catch (error) {reject(error);}});}// get 请求get(options) {let requestOptions = Object.assign({}, options, {method: 'GET',});return this.request(requestOptions);}// post 请求post(options) {let requestOptions = Object.assign({}, options, {method: 'POST',});return this.request(requestOptions);}// 处理并发请求all(...promises) {return Promise.all(promises);}// 处理登录过期timeoutHandler(res) {uni.clearStorageSync();uni.navigateTo({url: '/pages/login/index',});uni.showToast({title: '登录过期,请重新登录',icon: 'none',duration: 2000,});return res;}
}// 实例化
let WxRequest = new WxRequestClass();// 用来自定义配置,想怎么搞就怎么搞
const customInstance = (options) => {return new WxRequestClass(options);
};
export {customInstance
};export default WxRequest;五、封装优点总结
| 功能点 | 说明 |
|---|---|
| ✅ 全局统一配置 | BaseURL、超时时间、请求头集中管理 |
| ✅ 自动 token 注入 | 登录状态自动携带 |
| ✅ 登录过期处理 | 自动跳转、提示用户 |
| ✅ 并发 loading 管理 | 多请求只显示一个 loading |
| ✅ 防闪烁机制 | 连续请求无闪烁 |
| ✅ 支持无 loading 请求 | 灵活可控 |
| ✅ 支持多实例配置 | 方便扩展不同服务场景 |
