Promise封装是必须的,关键在于统一拦截、错误分流、自动token注入和loading管理;原生uni.request为回调式API,需封装解决嵌套深、难调试、无法await及错误处理分散等问题。
直接结论:Promise封装是必须的,但不能只套一层 Promise.resolve() 就算完事;关键在于统一拦截、错误分流、自动 token 注入和 loading 状态管理。
原生 uni.request 是回调式 API,嵌套深、难调试、无法 await、错误处理分散。不封装就写业务,等于裸写异步逻辑——每次都要手动写 success/fail,catch 无处落脚,loading 开关靠人肉维护。
封装的核心价值不是“看起来更现代”,而是解决四个实际问题:
uni.showLoading() / uni.hideLoading()
401、404、网络超时等错误归类处理,而不是每页都弹一遍模态框token,避免每个接口都手动拼 header.Authorization
api/user/info 这种调用能直接 await,配合 async setup() 写法更自然很多人封装后 POST 请求失败,返回 415 Unsupported Media Type,问题就出在 header 配置上。
注意以下三点:
method 大小写敏感:"POST" 不等于 "post",uni-app 只认小写字符串Content-Type 必须按请求体类型匹配:传 data: {a:1} 且是 POST 时,应设 {'Content-Type': 'application/json'};若传的是 FormData,则必须用 'multipart/form-data',且不能手动设 —— uni.request 会自动处理 boundaryAuthorization 写成 authorization 也能通,但为保兼容,统一用首字母大写标准写法示例中容易错写成:
if (method === 'post') { header['Content-Type'] = 'application/x-www-form-urlencoded' // ❌ 错!没序列化 data,后端收不到}
正确做法是:需要表单提交时,显式用 encodeURIComponent 拼接,或改用 uni.uploadFile 处理文件上传。
Promise 封装本身不难,难的是在 fail 和 complete 中正确 reject / resolve,并确保异常可捕获。
关键点:
fail 回调里必须 reject(err),不能只 console.log;否则 await api.xxx().catch(...) 永远进不去 catch
complete 里不能做 resolve 或 reject,它只负责清理(比如关 loading),否则会覆盖 success/fail 的结果fail,err.errMsg 是 "request:fail network error" 这类字符串,不能直接当 HTTP 状态码用try/catch 外层包一层 .catch(() => {}),防止未捕获的 Promise rejection 导致白屏(尤其在 Vue 3 setup 中)最小可用封装骨架:
export default function request(params) { uni.showLoading({ title: '加载中' }) return new Promise((resolve, reject) => { uni.request({ url: BASE_URL + params.url, method: (params.method || 'GET').toLowerCase(), header: { ...params.header, ...(params.method?.toUpperCase() === 'POST' ? { 'Content-Type': 'application/json' } : {}), Authorization: uni.getStorageSync('token') ? `Bearer ${uni.getStorageSync('token')}` : '' }, data: params.data, success: res => { if (res.statusCode >= 200 && res.statusCode < 300) { resolve(res.data) } else { reject(new Error(`HTTP ${res.statusCode}`)) } }, fail: err => reject(err), complete: () => uni.hideLoading() }) })}
现象:在 onLoad 里写 await api.getUser(),控制台报 Cannot read property 'then' of undefined。
根本原因只有两个:
return new Promise(...),或者在条件分支里漏了 return)await,却误以为函数本身会阻塞执行(JavaScript 异步本质不会变)更隐蔽的问题是:Vue 2 选项式 API 中,data 初始化早于 onLoad,如果你在 data() 里就调用了封装函数,那它一定还没挂载完成,this 上下文为空,uni.getStorageSync 可能拿不到值 —— 这类问题不会报错,但 token 总是空。
建议始终把 API 调用放在生命周期钩子(onLoad、onShow)或事件回调中,而非 data 或 computed 里。