在HBuilderX开发工具中开发UniApp时,经常会使用到Request请求,每一个接口请求时都要写一个uin.request,频繁且重复写这个代码。
不想重复写代码就要想到怎么去完美封装 request.js。
UniApp 请求封装,包含:
1.全局请求 / 响应拦截
2.统一 Loading 管理(支持多请求自动合并)
3.统一错误提示
4.支持多个 baseURL 自由切换
5.支持 Promise 化
6.支持请求头自动携带 Token及Key名称自定义
一、 完整封装代码 utils/request.js
/** * * uni-app 通用网络请求封装 * */ // 可配置多个基础URL(你可以无限加) const BASE_URL = { // 开发域名 dev: 'https://dev-api.xxx.com', // 测试域名 test: 'https://test-api.xxx.com', // 生产域名 prod: 'https://api.xxx.com', // 第三方接口域名(示例,可自行新增) third: 'https://other-api.xxx.com' } // 全局默认配置 const GLOBAL_CONFIG = { defaultEnv: 'dev', // 默认使用的域名key contentType: 'application/json', // 默认Content-Type timeout: 10000, // 默认超时时间 10秒 loadingText: '加载中...', //loading显示文本 tokenKey: 'token', //保存Token的Key loginPath: '/pages/login/login' //Token过期时跳转的路径 } // ==================== 状态管理 ==================== let loadingCount = 0; let loadingTimer = null; // ==================== Loading 管理 ==================== const showLoading = () => { loadingCount++; loadingTimer = setTimeout(() => { if (loadingCount > 0) { uni.showLoading({ title: GLOBAL_CONFIG.loadingText, mask: true }); } }, 200); } const hideLoading = () => { loadingCount--; if (loadingCount <= 0) { loadingCount = 0; clearTimeout(loadingTimer); uni.hideLoading(); } } // ==================== 跳转登录(防重复) ==================== let isRedirecting = false; const redirectToLogin = (tokenKey, loginPath) => { if (isRedirecting) return; isRedirecting = true; uni.removeStorageSync(tokenKey); // 延迟跳转,避免白屏 setTimeout(() => { uni.navigateTo({ url: loginPath }); isRedirecting = false; }, 100); } // 统一错误提示 const showToast = (msg, duration = 1500, icon = "none") => { uni.showToast({ title: msg || '请求失败', icon: icon, duration: duration }); } /** * 核心请求方法 * @param {object} options 请求配置 * @param {string} options.url - 接口地址 * @param {string} options.contentType Content-Type的设置 * @param {string} options.baseURL 使用哪个URL:base1/base2/base3 * @param {string} options.skipLoading 是否跳过 loading * @param {string} options.skipToken 是否跳过 Token(如登录接口) * @param {string} options.tokenKey 保存Token的Key * @param {string} options.loginPath Token过期时跳转的路径 * @param {string} options.skipErrorToast 是否跳过错误提示 * @param {number} options.timeout 本请求超时时间 */ const request = (options) => { // 拼接完整请求地址 let baseKey = options.baseURL || GLOBAL_CONFIG.defaultEnv; let baseUrl = BASE_URL[baseKey]; let url = options.url; //console.log(url) let fullUrl = url.startsWith('http') ? url : `${baseUrl}${url}`; //console.log(fullUrl) // 超时时间(单个请求可覆盖) let timeout = options.timeout || GLOBAL_CONFIG.timeout; // 是否跳过 loading let skipLoading = options.skipLoading || false; // 是否跳过 Token(如登录接口) let skipToken = options.skipToken || false; // 保存Token的Key let tokenKey = options.tokenKey || GLOBAL_CONFIG.tokenKey; // 是否跳过错误提示 let skipErrorToast = options.skipErrorToast || false; // 请求拦截 ====================================== // 1. 显示 loading if (!skipLoading) { showLoading(); } //Content-Type 优先级:接口自定义 > 全局默认 let contentType = options.contentType || GLOBAL_CONFIG.contentType // 可在这里加 token let header = { 'Content-Type': contentType, ...options.header }; // 从本地存储获取 Token let token = uni.getStorageSync(tokenKey); if (token) { header['Authorization'] = `Bearer ${token}`; } return new Promise((resolve, reject) => { uni.request({ url: fullUrl, method: options.method || 'GET', data: options.data || {}, header, timeout, success: (res) => { // 响应拦截 ================================== if (!skipLoading) { hideLoading(); } let statusCode = res.statusCode let data = res.data; // 状态码判断(根据你后端规则改) // 业务逻辑状态码判断 (假设后端返回格式为 { code: 200, msg: 'success', data: {} }) if (statusCode >= 200 && statusCode < 300) { resolve(data); } else if (statusCode === 401) { // Token 过期 redirectToLogin(); reject(data); } else if (statusCode === 403) { // 无权限 if (!skipErrorToast) { showToast('无访问权限', 2000); } reject(data) } else if (statusCode === 404) { if (!skipErrorToast) { showToast('请求资源不存在', 2000); } reject(data); } else { if (!skipErrorToast) { showToast(data.msg || data.message || '请求异常', 2000); } reject(data); } }, fail: (err) => { console.error('Request Fail:', err); if (!skipLoading) { hideLoading(); } let message = '网络异常,请检查网络'; if (err.errMsg) { if (err.errMsg.includes('timeout')) { message = '请求超时'; } } if (!skipErrorToast) { showToast(message, 2000); } reject(err); } }) }) } // 导出快捷方法 export default { request, /** GET请求 */ get: (url, data = {}, config = {}) => request({ url, method: 'GET', data, ...config }), /** POST请求 */ post: (url, data = {}, config = {}) => request({ url, method: 'POST', data, ...config }), /** PUT请求 */ put: (url, data = {}, config = {}) => request({ url, method: 'PUT', data, ...config }), /** DELETE请求 */ delete: (url, data = {}, config = {}) => request({ url, method: 'DELETE', data, ...config }), /** PATCH请求 */ patch: (url, data = {}, config = {}) => request({ url, method: 'PATCH', data, ...config }) }
二、全局挂载(main.js)
import request from './utils/request.js' // 全局挂载 uni.$http = request uni.$get = request.get uni.$post = request.post uni.$put = request.put uni.$delete = request.delete uni.$patch = request.patch
三、页面使用示例(超级简单)
// GET 请求
uni.$http.get('/user/info', {}, {
timeout: 30000 // 请求超时时间
}).then(res => {
console.log(res)
})
uni.$get('/user/info').then(res => {
console.log(res)
})
// POST 请求
uni.$http.post('/user/login', {
username: 'admin',
password: '123456'
}, {
baseURL: 'test', // 测试环境
skipToken: false, // 登录接口不需要 token
timeout: 30000 // 请求超时时间
}).then(res => {
console.log(res)
})
uni.$post('/user/login', {
username: 'admin',
password: '123456'
}, {
baseURL: 'test', // 测试环境
skipToken: false, // 登录接口不需要 token
timeout: 30000 // 请求超时时间
}).then(res => {
console.log(res)
})以上基本实现接口请求的统一封装了。











