news 2026/11/3 15:24:05

uni-app请求封装实践:提升开发效率与代码质量

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
uni-app请求封装实践:提升开发效率与代码质量

1. 为什么需要封装uni-app的request请求

在uni-app开发中,网络请求是最基础也最频繁使用的功能之一。原生提供的uni.request()虽然功能完善,但在实际项目开发中直接使用会面临几个典型问题:

  1. 代码冗余:每个请求都需要重复编写url、header、错误处理等逻辑
  2. 维护困难:当需要修改请求基础配置时,需要逐个修改所有请求
  3. 缺乏统一处理:无法集中管理请求拦截、响应拦截、错误处理等逻辑
  4. 可读性差:业务代码中混杂大量网络请求细节,影响代码整洁度

以一个典型场景为例,假设我们需要在多个页面调用用户信息接口:

// 页面A uni.request({ url: 'https://api.example.com/user/info', method: 'GET', header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token }, success(res) { // 处理数据 }, fail(err) { console.error(err) uni.showToast({ title: '网络错误' }) } }) // 页面B又得重复写一遍

这种写法在中小型项目中很快就会变得难以维护。而通过封装request,我们可以实现:

// 页面A api.getUserInfo().then(res => { // 处理数据 }).catch(err => { // 错误处理 }) // 页面B api.getUserInfo().then(...)

2. 基础封装方案设计

2.1 核心封装思路

一个完整的request封装应该包含以下核心功能:

  1. 基础配置:统一设置baseURL、超时时间等
  2. 请求拦截:统一添加token、设备信息等header
  3. 响应拦截:统一处理响应数据、错误码
  4. 错误处理:网络错误、业务错误统一处理
  5. 便捷方法:提供get/post等快捷方法

2.2 基础实现代码

创建request.js文件:

const BASE_URL = 'https://api.example.com' const request = (options) => { // 合并配置 options.url = BASE_URL + options.url options.header = options.header || {} options.timeout = options.timeout || 60000 // 添加公共header options.header['Content-Type'] = 'application/json' const token = uni.getStorageSync('token') if (token) { options.header['Authorization'] = 'Bearer ' + token } return new Promise((resolve, reject) => { options.success = (res) => { // 响应拦截处理 if (res.statusCode === 200) { resolve(res.data) } else { reject(res) } } options.fail = (err) => { // 错误处理 uni.showToast({ title: '网络错误', icon: 'none' }) reject(err) } uni.request(options) }) } // 快捷方法 const get = (url, data, options = {}) => { return request({ url, data, method: 'GET', ...options }) } const post = (url, data, options = {}) => { return request({ url, data, method: 'POST', ...options }) } export default { request, get, post }

3. 高级功能实现

3.1 请求拦截器实现

在实际项目中,我们通常需要在请求发出前做一些统一处理:

// 请求拦截器队列 const requestInterceptors = [] // 添加拦截器 const addRequestInterceptor = (interceptor) => { requestInterceptors.push(interceptor) } // 修改request函数 const request = (options) => { // ...原有代码... // 执行请求拦截器 requestInterceptors.forEach(interceptor => { options = interceptor(options) || options }) // ...剩余代码... }

使用示例:

// 添加设备信息拦截器 addRequestInterceptor((config) => { config.header['Device-Id'] = uni.getSystemInfoSync().deviceId return config })

3.2 响应拦截器实现

同样地,我们可以实现响应拦截:

const responseInterceptors = [] const addResponseInterceptor = (interceptor) => { responseInterceptors.push(interceptor) } // 修改request的success处理 options.success = (res) => { let response = res responseInterceptors.forEach(interceptor => { response = interceptor(response) || response }) if (response.statusCode === 200) { resolve(response.data) } else { reject(response) } }

使用示例:

// 添加业务错误码拦截器 addResponseInterceptor((response) => { if (response.data.code === 401) { // token过期处理 uni.navigateTo({ url: '/pages/login/login' }) return null } return response })

3.3 取消请求功能

在某些场景下(如页面卸载时),我们需要取消未完成的请求:

const request = (options) => { let requestTask = null const promise = new Promise((resolve, reject) => { // ...其他代码... requestTask = uni.request({ ...options, success: (res) => { // ...响应处理... }, fail: (err) => { // ...错误处理... } }) }) // 添加abort方法 promise.abort = () => { requestTask && requestTask.abort() } return promise }

使用示例:

const req = api.getUserInfo() req.then(...).catch(...) // 需要取消时 req.abort()

4. 完整实现与最佳实践

4.1 完整封装代码

结合上述所有功能,完整的request封装如下:

class Request { constructor() { this.BASE_URL = 'https://api.example.com' this.requestInterceptors = [] this.responseInterceptors = [] } request(options) { let requestTask = null // 合并配置 options.url = this.BASE_URL + options.url options.header = options.header || {} options.timeout = options.timeout || 60000 // 请求拦截 this.requestInterceptors.forEach(interceptor => { options = interceptor(options) || options }) const promise = new Promise((resolve, reject) => { options.success = (res) => { // 响应拦截 let response = res this.responseInterceptors.forEach(interceptor => { response = interceptor(response) || response }) if (response && response.statusCode === 200) { resolve(response.data) } else if (response) { reject(response) } } options.fail = (err) => { uni.showToast({ title: '网络错误', icon: 'none' }) reject(err) } requestTask = uni.request(options) }) promise.abort = () => { requestTask && requestTask.abort() } return promise } get(url, data, options = {}) { return this.request({ url, data, method: 'GET', ...options }) } post(url, data, options = {}) { return this.request({ url, data, method: 'POST', ...options }) } addRequestInterceptor(interceptor) { this.requestInterceptors.push(interceptor) } addResponseInterceptor(interceptor) { this.responseInterceptors.push(interceptor) } } const api = new Request() // 添加默认拦截器 api.addRequestInterceptor((config) => { config.header['Content-Type'] = 'application/json' const token = uni.getStorageSync('token') if (token) { config.header['Authorization'] = 'Bearer ' + token } return config }) api.addResponseInterceptor((response) => { if (response.data.code === 401) { uni.navigateTo({ url: '/pages/login/login' }) return null } return response }) export default api

4.2 使用示例

import api from '@/utils/request' // 获取用户信息 api.get('/user/info').then(res => { console.log(res) }).catch(err => { console.error(err) }) // 提交表单 api.post('/form/submit', { name: '张三', age: 25 }).then(...) // 添加自定义拦截器 api.addRequestInterceptor((config) => { console.log('请求发出:', config.url) return config })

5. 常见问题与解决方案

5.1 跨域问题处理

在H5端开发时,可能会遇到跨域问题。解决方案:

  1. 开发环境:
    • 配置devServer代理
    • 在manifest.json中配置:
{ "h5": { "devServer": { "proxy": { "/api": { "target": "https://api.example.com", "changeOrigin": true, "pathRewrite": { "^/api": "" } } } } } }
  1. 生产环境:
    • 使用Nginx反向代理
    • 后端配置CORS

5.2 请求重试机制

对于不稳定的网络环境,可以实现自动重试:

const requestWithRetry = async (options, retryCount = 3) => { try { return await request(options) } catch (err) { if (retryCount > 0) { await new Promise(resolve => setTimeout(resolve, 1000)) return requestWithRetry(options, retryCount - 1) } throw err } }

5.3 文件上传下载

对于文件操作,可以单独封装:

const uploadFile = (url, filePath, formData = {}) => { return new Promise((resolve, reject) => { uni.uploadFile({ url: BASE_URL + url, filePath, name: 'file', formData, success: resolve, fail: reject }) }) } const downloadFile = (url) => { return new Promise((resolve, reject) => { uni.downloadFile({ url: BASE_URL + url, success: resolve, fail: reject }) }) }

5.4 性能优化建议

  1. 合理设置缓存:对于不常变的数据,可以添加缓存机制
  2. 请求合并:对于同页面多个请求,可以考虑合并请求
  3. 取消无用请求:在页面卸载时取消未完成的请求
  4. 合理设置超时时间:根据接口特点设置不同的超时时间
// 缓存示例 const cache = new Map() const getWithCache = async (url) => { if (cache.has(url)) { return cache.get(url) } const res = await get(url) cache.set(url, res) return res }

6. 实际项目中的扩展

6.1 TypeScript支持

对于使用TypeScript的项目,可以添加类型定义:

interface RequestConfig { url: string method?: 'GET' | 'POST' | 'PUT' | 'DELETE' data?: any header?: Record<string, string> timeout?: number } interface ResponseData<T = any> { code: number message: string data: T } class Request { // ...其他代码... async request<T = any>(options: RequestConfig): Promise<ResponseData<T>> { // ...实现... } }

6.2 多环境配置

在实际项目中,通常会有多个环境:

const env = process.env.NODE_ENV || 'development' const BASE_URL_MAP = { development: 'https://dev.api.example.com', test: 'https://test.api.example.com', production: 'https://api.example.com' } class Request { constructor() { this.BASE_URL = BASE_URL_MAP[env] // ...其他初始化... } // ...其他代码... }

6.3 监控与日志

可以添加请求监控和日志记录:

api.addRequestInterceptor((config) => { console.log('[Request]', config.method, config.url) return config }) api.addResponseInterceptor((response) => { if (response) { console.log('[Response]', response.statusCode, response.data) } return response })

对于线上环境,可以将日志上报到服务器:

api.addResponseInterceptor((response) => { if (response.statusCode >= 500) { reportError({ url: response.config.url, status: response.statusCode, data: response.data }) } return response })

7. 与其他方案的对比

7.1 与直接使用uni.request对比

特性封装方案直接使用uni.request
代码复用性高低
统一配置支持不支持
拦截器机制支持不支持
类型支持(TypeScript)容易困难
学习成本略高低

7.2 与第三方库(flyio/axios)对比

uni-app生态中有一些第三方请求库,如flyio,它们的主要特点:

  1. flyio:

    • 支持请求/响应拦截
    • 支持Promise
    • 体积小巧
    • 但更新维护不够活跃
  2. axios:

    • 功能强大
    • 但在uni-app中需要适配
    • 部分功能在小程序中不可用

自封装方案的优势:

  • 完全可控,可以根据项目需求定制
  • 不依赖第三方库,减少潜在风险
  • 更贴合uni-app的特性

8. 测试与调试技巧

8.1 Mock数据方案

在开发阶段,可以使用Mock数据:

// 添加Mock拦截器 if (process.env.NODE_ENV === 'development') { api.addRequestInterceptor((config) => { if (config.url.startsWith('/mock/')) { config.url = config.url.replace('/mock/', '/') config.baseURL = '' // 清除baseURL return { ...config, success: (res) => { // 直接返回Mock数据 res.data = require(`@/mock${config.url}.json`) config.success(res) } } } return config }) }

8.2 调试技巧

  1. 查看完整请求信息:
api.addRequestInterceptor((config) => { console.log('Request Config:', JSON.stringify(config, null, 2)) return config })
  1. 性能分析:
api.addRequestInterceptor((config) => { config._startTime = Date.now() return config }) api.addResponseInterceptor((response) => { if (response) { const duration = Date.now() - response.config._startTime console.log(`请求 ${response.config.url} 耗时: ${duration}ms`) } return response })
  1. 错误追踪:
api.addResponseInterceptor((response) => { if (response && response.statusCode >= 400) { console.error('请求错误:', { url: response.config.url, status: response.statusCode, data: response.data }) } return response })

9. 安全注意事项

  1. Token安全:

    • 不要将token存储在全局变量中
    • 使用uni.setStorageSync加密存储
    • 设置合理的过期时间
  2. HTTPS:

    • 生产环境必须使用HTTPS
    • 在manifest.json中配置:
{ "networkTimeout": { "request": 60000, "uploadFile": 60000, "downloadFile": 60000 }, "app-plus": { "sslVerify": true } }
  1. 输入验证:
    • 对所有接口返回数据进行验证
    • 使用TypeScript接口定义帮助验证
interface UserInfo { id: number name: string avatar?: string } const getUserInfo = async (): Promise<UserInfo> => { const res = await api.get('/user/info') // TypeScript会帮助检查返回数据是否符合接口定义 return res.data }
  1. 防重放攻击:
    • 对重要请求添加nonce/timestamp
    • 在请求拦截器中实现:
api.addRequestInterceptor((config) => { if (config.method === 'POST') { config.data = config.data || {} config.data.timestamp = Date.now() config.data.nonce = Math.random().toString(36).substring(2) } return config })

10. 项目结构建议

对于大型项目,建议的请求相关代码结构:

src/ api/ modules/ user.js # 用户相关接口 product.js # 产品相关接口 order.js # 订单相关接口 request.js # 封装的request类 index.js # 统一导出所有api

user.js示例:

import api from '../request' export default { getInfo() { return api.get('/user/info') }, updateInfo(data) { return api.post('/user/update', data) } }

index.js示例:

import user from './modules/user' import product from './modules/product' import order from './modules/order' export default { user, product, order }

使用方式:

import api from '@/api' api.user.getInfo().then(...)

这种结构的好处:

  • 接口按业务模块划分,清晰明了
  • 便于团队协作,不同开发者负责不同模块
  • 便于维护和查找接口
  • 便于统一修改和重构

11. 性能优化进阶

11.1 请求优先级管理

对于复杂页面,可以实施请求优先级策略:

class Request { constructor() { this.pendingRequests = [] this.maxConcurrent = 5 // 最大并发数 this.currentConcurrent = 0 } async request(options) { if (this.currentConcurrent >= this.maxConcurrent) { await new Promise(resolve => { this.pendingRequests.push(resolve) }) } this.currentConcurrent++ try { // ...执行请求... } finally { this.currentConcurrent-- if (this.pendingRequests.length) { this.pendingRequests.shift()() } } } }

11.2 请求去重

对于相同请求,可以避免重复发送:

const requestCache = new Map() const getCacheKey = (config) => { return `${config.method}:${config.url}:${JSON.stringify(config.data)}` } class Request { async request(options) { const cacheKey = getCacheKey(options) if (requestCache.has(cacheKey)) { return requestCache.get(cacheKey) } const promise = this._request(options) requestCache.set(cacheKey, promise) // 请求完成后清除缓存 promise.finally(() => { requestCache.delete(cacheKey) }) return promise } _request(options) { // 实际请求逻辑 } }

11.3 预加载策略

对于关键数据,可以在页面加载前预加载:

// 在App.vue中 export default { onLaunch() { // 预加载用户信息 api.user.getInfo().then(res => { store.commit('setUserInfo', res.data) }).catch(() => {}) } }

12. 错误监控与上报

完善的错误监控体系应包括:

  1. 前端错误捕获:
api.addResponseInterceptor((response) => { if (!response) return if (response.statusCode >= 500) { trackError({ type: 'API_ERROR', url: response.config.url, status: response.statusCode, message: response.data?.message || 'Unknown error' }) } return response })
  1. 网络错误监控:
options.fail = (err) => { trackError({ type: 'NETWORK_ERROR', url: options.url, message: err.errMsg || 'Network error' }) reject(err) }
  1. 性能监控:
api.addRequestInterceptor((config) => { config._startTime = performance.now() return config }) api.addResponseInterceptor((response) => { if (response) { const duration = performance.now() - response.config._startTime trackPerformance({ url: response.config.url, duration: Math.round(duration) }) } return response })

13. 国际化支持

对于多语言项目,可以在请求头中添加语言信息:

api.addRequestInterceptor((config) => { const locale = uni.getLocale() config.header['Accept-Language'] = locale return config })

对于错误消息,可以在响应拦截器中统一处理:

api.addResponseInterceptor((response) => { if (response && response.data && response.data.message) { response.data.message = i18n.t(response.data.message) } return response })

14. 版本兼容性处理

随着uni-app版本更新,需要注意:

  1. API兼容性:
    • 检查使用的uni.request参数在不同平台的兼容性
    • 必要时添加平台判断:
const isWeapp = uni.getSystemInfoSync().platform === 'devtools' if (isWeapp) { // 微信小程序特有配置 options.enableHttp2 = true }
  1. 新特性检测:
    • 对于新版本添加的功能,先检测是否可用:
if (uni.canIUse('request.onHeadersReceived')) { // 使用新特性 }
  1. 降级方案:
    • 对于不支持的功能,提供降级方案
try { const requestTask = uni.request(options) // 新版本功能 if (requestTask.onHeadersReceived) { requestTask.onHeadersReceived(...) } } catch (e) { // 降级处理 }

15. 单元测试建议

对于封装的request模块,建议编写单元测试验证:

describe('Request', () => { let api beforeEach(() => { api = new Request() api.BASE_URL = 'https://test.api' }) it('should add base url', async () => { const mockRequest = jest.spyOn(uni, 'request') mockRequest.mockImplementation(({success}) => { success({ statusCode: 200, data: {} }) }) await api.get('/test') expect(mockRequest).toBeCalledWith( expect.objectContaining({ url: 'https://test.api/test' }) ) }) it('should handle errors', async () => { const mockRequest = jest.spyOn(uni, 'request') mockRequest.mockImplementation(({fail}) => { fail({ errMsg: 'request:fail' }) }) await expect(api.get('/test')).rejects.toEqual({ errMsg: 'request:fail' }) }) })

测试要点应包括:

  • 基础功能测试(GET/POST等)
  • 拦截器测试
  • 错误处理测试
  • 取消请求测试
  • 超时处理测试

16. 未来扩展方向

随着业务发展,request封装可以进一步扩展:

  1. GraphQL支持:

    • 添加GraphQL客户端功能
    • 统一处理GraphQL错误
  2. WebSocket封装:

    • 提供更易用的WebSocket API
    • 自动重连、心跳检测等
  3. 离线缓存:

    • 对于重要数据实现离线优先策略
    • 使用IndexedDB或本地存储
  4. 请求优先级:

    • 实现请求优先级队列
    • 关键请求优先发送
  5. 数据压缩:

    • 对大请求体进行压缩
    • 减少数据传输量

17. 个人实践经验分享

在实际项目中,我总结了以下几点经验:

  1. 合理设置超时时间:

    • 普通接口:5-10秒
    • 文件上传:30-60秒
    • 关键接口:适当延长
  2. token刷新策略:

    • 检测到token过期时自动刷新
    • 避免多个并发请求同时触发刷新
let isRefreshing = false let refreshSubscribers = [] api.addResponseInterceptor(async (response) => { if (response.data.code === 401 && !isRefreshing) { isRefreshing = true try { const newToken = await refreshToken() uni.setStorageSync('token', newToken) refreshSubscribers.forEach(cb => cb(newToken)) refreshSubscribers = [] return api.request(response.config) // 重试原请求 } finally { isRefreshing = false } } else if (isRefreshing && response.config.url !== '/refreshToken') { // 将请求加入队列,等待token刷新完成后重试 return new Promise((resolve) => { refreshSubscribers.push((token) => { response.config.header.Authorization = `Bearer ${token}` resolve(api.request(response.config)) }) }) } return response })
  1. 请求取消策略:
    • 页面卸载时取消未完成请求
    • 路由切换时取消非必要请求
// 在页面onUnload中 onUnload() { this._requests.forEach(req => req.abort()) this._requests = [] }
  1. 性能监控:
    • 记录关键接口响应时间
    • 设置性能阈值报警
api.addResponseInterceptor((response) => { const duration = Date.now() - response.config._startTime if (duration > 2000) { trackSlowRequest({ url: response.config.url, duration }) } return response })
  1. 调试技巧:
    • 开发环境打印完整请求/响应信息
    • 使用vConsole等工具查看网络请求
if (process.env.NODE_ENV === 'development') { api.addRequestInterceptor((config) => { console.groupCollapsed(`Request: ${config.method} ${config.url}`) console.log('Config:', config) return config }) api.addResponseInterceptor((response) => { if (response) { console.log('Response:', response) console.groupEnd() } return response }) }

18. 总结

通过本文的封装方案,我们实现了:

  1. 统一管理:所有API请求集中管理,便于维护
  2. 功能完善:支持拦截器、取消请求、错误处理等高级功能
  3. 开发高效:简化业务代码,提高开发效率
  4. 安全可靠:内置安全措施,减少潜在风险
  5. 扩展性强:易于添加新功能和适配业务变化

在实际项目中,建议根据团队和项目特点调整封装方案,找到最适合的实现方式。一个好的请求封装应该随着项目发展而不断演进,持续满足业务需求。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/3 15:24:04

BurpSuite渗透测试实战:从环境搭建到SQL注入攻防

1. 项目概述&#xff1a;BurpSuite渗透测试实战环境搭建 作为Web安全领域的瑞士军刀&#xff0c;BurpSuite在渗透测试中占据着不可替代的地位。我至今记得第一次用Burp拦截到HTTP请求时的震撼——原来客户端与服务器的对话可以如此直观地呈现和操控。本文将基于Pikachu、DVWA等…

作者头像 李华
网站建设 2026/11/3 15:23:32

蜂窝网络仿真场景设计与优化实践指南

1. 蜂窝网络仿真场景设计基础蜂窝网络仿真是无线通信系统设计与优化的重要工具&#xff0c;它通过数字建模方式模拟真实网络环境中的各种物理和逻辑行为。与传统的实地测试相比&#xff0c;仿真技术能够在虚拟环境中快速构建和验证多种网络配置方案&#xff0c;大幅降低测试成本…

作者头像 李华
网站建设 2026/11/3 15:22:29

Unity安装四道硬门槛:Hub注册、许可证、模块依赖与路径编码避坑指南

简介&#xff1a;本资源是一份面向Unity初学者与开发新手的系统化安装与入门指南&#xff0c;解决从环境搭建到项目创建全流程中的常见卡点问题。文档以Step-by-step方式详解Unity Hub下载、账号注册、个人版许可证激活、多版本编辑器安装&#xff08;含Android模块等关键组件选…

作者头像 李华
网站建设 2026/11/3 15:21:46

中小微企业低成本高防IP部署实战指南

1. 中小微企业高防IP部署的必要性中小企业在数字化转型过程中&#xff0c;网站和在线业务已成为核心资产。但随之而来的DDoS攻击威胁与日俱增&#xff0c;一次成功的攻击可能导致业务中断数小时甚至数天&#xff0c;造成直接经济损失和品牌信誉损害。传统企业级高防方案动辄数十…

作者头像 李华
网站建设 2026/11/3 15:21:04

Paperclip 技术解析:从文件聚合工具到 AI 工作流编排的轻量实践

1. 从“paperclip”这个词说起&#xff1a;它到底指什么 第一次看到“paperclip”这个词&#xff0c;很多人脑子里蹦出来的画面就是办公桌上那枚弯弯的金属回形针。但如果它出现在技术社区、项目标题或者产品命名里&#xff0c;含义就完全不一样了。我接触过不少以“paperclip”…

作者头像 李华
网站建设 2026/11/3 15:20:54

POCO C++库中文文档深度解析:跨平台开发的可控性设计哲学

简介&#xff1a;本资源为C POCO库官方中文文档PDF&#xff0c;面向中高级C开发者、嵌入式系统工程师及跨平台应用框架学习者&#xff0c;解决标准C缺乏成熟网络、日志、XML解析、多线程封装等企业级能力的问题。文档系统覆盖Foundation&#xff08;核心抽象、智能指针、Unicod…

作者头像 李华