封装uni.request的本质是统一控制请求生命周期。它通过集中处理加载态、token携带、URL补全、错误分类(如401跳登录)、超时与重复请求等共性逻辑,避免重复代码散落各处,提升可维护性与健壮性。

uni.request 封装的本质是统一控制请求生命周期
直接说结论:不封装 uni.request,项目越往后越难维护——不是它不能用,而是每次手动写 uni.showLoading、拼 baseUrl、塞 Authorization、判 statusCode,等于把重复逻辑散落在二十个页面里。封装不是炫技,是把「所有请求共有的行为」收口到一处。
关键点在于:你真正需要的不是“一个能发请求的函数”,而是一个能自动处理加载态、错误跳转、token 携带、URL 补全的请求入口。比如 token 过期时,所有接口都该跳登录页,而不是每个 .catch() 里都写一遍 uni.navigateTo({ url: '/pages/login/index' })。
怎么写一个够用又不易翻车的 request.js
推荐路径:common/http/request.js(别放 utils 或 until,后者是错别字高发区)。
- 用
Promise包一层,必须返回resolve(res.data),而不是整个response,否则业务层永远要写res.data.list -
baseUrl建议从环境变量读取,开发/测试/生产切域名不用改代码:process.env.NODE_ENV === 'development' ? 'https://dev.api.com' : 'https://api.com' - header 自动注入 token:
'Authorization': uni.getStorageSync('token') || '',但注意:如果接口明确不需要认证(如登录、注册),得留个开关绕过 - 一定要加
uni.hideLoading()在complete钩子里,而不是只在success里——失败时 loading 会一直卡住 - 别在封装里写
uni.showToast全局提示,那是业务逻辑;封装只做「统一失败响应分类」,比如401触发清理 token + 跳登录,500只reject让调用方自己决定怎么提示
为什么 api.js 要和 request.js 分开
因为 request.js 是“轮子”,api.js(或 api/index.js)是“说明书”。前者管怎么发,后者管发什么。
常见错误是把所有接口 URL 和参数硬编码进页面:this.$request('/user/info?id=' + id)。这会导致三处问题:URL 写错没提示、参数名改了搜不到、无法统一加埋点或日志。
- 在
api/user.js里写:export const getUserInfo = (id) => request({ url: '/user/info', data: { id } }) - 页面里只导入并调用:
import { getUserInfo } from '@/api/user',连 URL 字符串都看不到 - 如果某天后端把
/user/info改成/v2/user/profile,你只改一个文件,而不是全局搜索替换 - 别用
export default { getUserInfo() {} },命名导出会更易 tree-shaking,也方便 IDE 自动导入
容易被忽略的坑:超时、重复请求、跨域(小程序除外)
这些不是“高级功能”,而是上线后必爆的问题。
-
timeout默认是 60s,但用户等 5 秒没反应就会关页面。建议设为10000(10秒),并在超时后reject明确错误类型,比如{ code: 'TIMEOUT', msg: '请求超时,请重试' } - 防重复提交不是 UI 层禁按钮就够了——用户快速点两次,两个请求仍会发出。可在
request.js里用Map缓存请求 key(如url+method+JSON.stringify(data)),二次发起前先检查是否 pending - 跨域只在 H5 端存在,App 和小程序走的是原生网络栈。H5 调用非同源接口必须配代理(
vue.config.js的devServer.proxy),否则控制台报CORS错误,但fail回调里拿不到具体原因,只会显示request:fail net::ERR_FAILED
最麻烦的其实是「请求成功但业务失败」:比如后端返回 { code: 40001, msg: '余额不足' }。这种不能靠 HTTP 状态码拦截,得在 request.js 的 success 里判断 res.data.code,再统一 reject,否则每个页面都要 if-else。

















