在 UniApp 中封装 uni.request
实现全局使用,可通过以下方案实现 统一配置、拦截器管理、错误处理和全局调用。以下是完整实现步骤:
一、基础封装方案(推荐)
1. 创建 http.js
请求封装模块
// utils/http.js
// 基础配置
const BASE_URL = 'https://api.example.com'
const TIMEOUT = 15000
// 请求队列(用于防止重复提交)
const pendingRequests = new Map()
// 生成请求唯一标识
const generateReqKey = (config) => {
return `${config.url}&${config.method}`
}
// 核心请求函数
const request = (options) => {
// 合并配置
const config = {
url: options.url,
data: options.data || {},
method: options.method || 'GET',
header: {
'Content-Type': 'application/json',
'X-Token': uni.getStorageSync('token') || '', // 自动携带token
...(options.header || {}),
},
timeout: TIMEOUT,
...options,
}
// 处理完整URL
if (!config.url.startsWith('http')) {
config.url = BASE_URL + config.url
}
// 防止重复请求拦截
const reqKey = generateReqKey(config)
if (pendingRequests.has(reqKey)) {
return Promise.reject({ errMsg: '重复请求已取消' })
}
pendingRequests.set(reqKey, config)
return new Promise((resolve, reject) => {
uni.request({
...config,
success: (res) => {
// 请求完成后移除队列
pendingRequests.delete(reqKey)
// 全局响应拦截器
if (res.statusCode === 200) {
// 业务状态码处理(示例)
if (res.data.code === 401) {
uni.navigateTo({ url: '/pages/login/login' })
return reject(res.data)
}
resolve(res.data)
} else {
// HTTP状态码错误处理
reject({
code: res.statusCode,
message: `请求失败: ${res.errMsg}`
})
}
},
fail: (err) => {
pendingRequests.delete(reqKey)
// 网络错误统一处理
reject({
code: -1,
message: '网络连接失败,请检查网络'
})
},
complete: () => {
// 可添加全局Loading关闭逻辑
uni.hideLoading()
}
})
})
}
// 封装常用方法
const http = {
get: (url, params = {}, options = {}) => {
return request({
url,
data: params,
method: 'GET',
...options
})
},
post: (url, data = {}, options = {}) => {
return request({
url,
data,
method: 'POST',
...options
})
},
put: (url, data = {}, options = {}) => {
// 其他方法类似封装
}
}
export default http
2. 全局挂载(main.js)
// main.js
import Vue from 'vue'
import App from './App'
import http from '@/utils/http'
// 挂载到Vue原型
Vue.prototype.$http = http
// 挂载到全局uni对象
uni.$http = http
// 如需使用this.$http调用
Vue.use({
install(Vue) {
Vue.config.globalProperties.$http = http
}
})
3. 页面中使用
// 在.vue文件中
export default {
methods: {
async fetchData() {
try {
// GET请求
const res1 = await this.$http.get('/user', { id: 123 })
// POST请求
const res2 = await uni.$http.post('/login', {
username: 'admin',
password: '123456'
})
} catch (err) {
uni.showToast({ title: err.message, icon: 'none' })
}
}
}
}
二、高级功能扩展
1. 添加拦截器系统
// 在http.js中添加
const interceptors = {
request: [],
response: []
}
// 注册拦截器
http.addRequestInterceptor = (interceptor) => {
interceptors.request.push(interceptor)
}
http.addResponseInterceptor = (interceptor) => {
interceptors.response.push(interceptor)
}
// 修改request函数
const request = async (options) => {
// ...原有代码...
// 执行请求拦截器
for (const interceptor of interceptors.request) {
config = await interceptor(config) || config
}
return new Promise((resolve, reject) => {
uni.request({
...config,
success: async (res) => {
// 执行响应拦截器
let response = res
for (const interceptor of interceptors.response) {
response = await interceptor(response) || response
}
// ...后续处理...
}
})
})
}
2. 使用示例(Token刷新)
// 在main.js中注册拦截器
http.addRequestInterceptor(config => {
if (!config.url.includes('/refresh-token')) {
const token = uni.getStorageSync('token')
if (token) config.header['X-Token'] = token
}
return config
})
http.addResponseInterceptor(async res => {
if (res.data.code === 401) {
// Token过期自动刷新
const newToken = await http.post('/refresh-token', {
refreshToken: uni.getStorageSync('refreshToken')
})
uni.setStorageSync('token', newToken)
// 重新发送原请求
return http(res.config)
}
return res
})
三、多端适配要点
H5跨域处理
// 开发环境代理配置 (vue.config.js) devServer: { proxy: { '/api': { target: 'https://api.example.com', changeOrigin: true, pathRewrite: { '^/api': '' } } } }
小程序域名白名单
需在
manifest.json
配置合法域名
"mp-weixin": { "appid": "xxx", "setting": { "urlCheck": false }, "permission": { "scope.userLocation": { "desc": "需要获取您的位置信息" } }, "requiredPrivateInfos": ["getLocation"] }
App端证书校验
// 仅iOS需要处理 if (uni.getSystemInfoSync().platform === 'ios') { config.sslVerify = false // 关闭SSL验证(仅测试环境) }
四、最佳实践建议
全局Loading控制
// 请求计数 let requestCount = 0 http.addRequestInterceptor(config => { if (!config.hideLoading) { requestCount === 0 && uni.showLoading({ title: '加载中', mask: true }) requestCount++ } return config }) http.addResponseInterceptor(response => { if (!response.config.hideLoading) { requestCount-- requestCount === 0 && uni.hideLoading() } return response })
请求重试机制
const retryRequest = (config, retry = 3) => { return request(config).catch(err => { return retry > 0 ? retryRequest(config, retry - 1) : Promise.reject(err) }) }
TypeScript支持
// http.d.ts declare module '@vue/runtime-core' { interface ComponentCustomProperties { $http: { get<T = any>(url: string, params?: object): Promise<T>; post<T = any>(url: string, data?: object): Promise<T>; } } }
封装优势:
统一管理所有网络请求逻辑
自动处理 token 和权限认证
支持多端差异化配置
提供拦截器实现业务解耦
内置防重复提交和错误重试机制