当前位置: 首页 > news >正文

uni-app 请求封装

一、封装目的

在 uni-app 项目中,接口请求频繁且业务逻辑复杂。
如果每次都直接调用 uni.request,会遇到以下问题:

  • ❌ 每个页面都要手动配置 BaseURL、Header 等公共参数;

  • ❌ 不能统一处理登录过期、token 校验等逻辑;

  • ❌ 无法优雅地控制 loading 状态(并发、闪烁问题);

  • ❌ 错误提示风格不一致;

  • ❌ 重复代码过多,维护成本高。

为了提升开发体验与可维护性,我们对 uni.request 进行二次封装,
实现以下功能:

功能项说明
全局默认配置统一 BaseURL、超时时间、请求头
请求/响应拦截器统一 token 注入与响应处理
登录过期处理自动跳转登录页、清除缓存
loading 控制并发请求只显示一个 loading,避免闪烁
自定义实例可快速创建带不同配置的请求实例
GET/POST 快捷方法简化调用形式

二、代码结构概览

文件结构建议如下:

/utils├─ env.js              // 环境配置(开发/生产)├─ request.js          // 本封装的核心文件

env.js 示例

export default {baseURL: 'http://123.56.125.118:8300'
};

三、核心实现逻辑

1️⃣ 默认配置管理

defaultProps = {BaseURL: env.baseURL,timeout: 10000,header: {},isLoading: true, // 是否显示loading
};
  • BaseURL:项目全局接口地址,统一管理。

  • timeout:请求超时时间(毫秒)。

  • header:默认请求头,可在实例化时追加。

  • isLoading:是否在该请求中显示加载提示,可在单个请求中控制。


2️⃣ 请求与响应拦截器

请求拦截(request)

在每次发起请求前自动携带 token。

request: (options) => {let token = uni.getStorageSync('token');if (token) options.header['token'] = token;return options;
}

📘 意义
无需在每个请求中手动写入 token,实现统一认证。


响应拦截(response)

对后端返回的数据进行集中处理。

response: (res) => {if (res.isSuccess) {let code = res.data.code;switch (code) {case 200:return res.data;case 208:case 401:return this.timeoutHandler(res.data);default:uni.showToast({title: '程序出现错误',icon: 'none'});}} else {uni.showToast({title: '请求失败',icon: 'none'});return Promise.reject(res);}
}

📘 意义
统一处理后端业务状态码,例如:

  • 200:成功返回;

  • 401 / 208:登录过期,触发跳转;

  • 其他状态:友好提示。


3️⃣ Loading 并发控制与防闪烁

核心机制:

// 当前进行中的请求数量
loadingCount = 0;// 防止连续请求时闪烁
timer = null;
逻辑说明
场景行为
第一个请求开始显示 Loading
并发多个请求loadingCount 递增,不重复显示
每个请求完成loadingCount 递减
所有请求结束延迟 400ms 后隐藏 Loading(防止闪烁)
实现代码
if (requestOptions.isLoading) {if (this.timer) {clearTimeout(this.timer);this.timer = null;}this.loadingCount++;if (this.loadingCount === 1) {uni.showLoading({ title: '加载中', mask: true });}
}

并在请求的 complete 回调中计数递减:

complete: () => {if (requestOptions.isLoading) {this.loadingCount = Math.max(this.loadingCount - 1, 0);if (this.loadingCount === 0) {this.timer = setTimeout(() => {uni.hideLoading();this.timer = null;}, 400);}}
}

📘 意义
解决了「并发多次 loading」与「连续请求闪烁」两大问题,
提升交互流畅度。


4️⃣ 登录过期自动处理

timeoutHandler(res) {uni.clearStorageSync();uni.navigateTo({ url: '/pages/login/index' });uni.showToast({title: '登录过期,请重新登录',icon: 'none',});return res;
}

📘 意义
后端返回 401/208 时,自动清空缓存并跳转登录页,
无需在业务层单独判断。


5️⃣ 提供简洁调用方法

get(options) {return this.request({ ...options, method: 'GET' });
}post(options) {return this.request({ ...options, method: 'POST' });
}

📘 意义
简化业务层调用,如:

WxRequest.get({ url: '/user/info' });
WxRequest.post({ url: '/login', data: form });

6️⃣ 自定义实例化

const customInstance = (options) => {return new WxRequestClass(options);
};

📘 意义
当你需要访问不同后端服务或配置(如文件上传、mock 服务)时:

const uploadRequest = customInstance({BaseURL: 'http://upload.api.com',header: { 'Content-Type': 'multipart/form-data' }
});

四、使用示例

1️⃣ 基础 GET 请求

WxRequest.get({url: '/user/profile',
}).then(res => {console.log('用户资料', res);
});

2️⃣ POST 请求(带参数)

WxRequest.post({url: '/login',data: {username: 'admin',password: '123456'}
});

3️⃣ 禁止 loading 的请求

WxRequest.get({url: '/home/banner',isLoading: false // 不显示 loading
});

4️⃣ 并发请求示例

WxRequest.all(WxRequest.get({ url: '/user/info' }),WxRequest.get({ url: '/user/setting' })
).then(([info, setting]) => {console.log(info, setting);
});

📘 效果
两个请求同时发起,只显示一个“加载中”,
等两个请求都完成后再隐藏。

完整代码

import env from './env.js';class WxRequestClass {// 存放一些默认配置:根地址、超时时间、请求头,这些属于全局配置一般情况下是不会改变的defaultProps = {BaseURL: env.baseURL,timeout: 10000,header: {},isLoading: true, // 是否显示loading效果};// 用来解决高并发请求时,从开始请求显示loading,到全部完成隐藏loadingloadingCount = 0;// 解决连续请求时,看到的闪烁loading问题 (相当于防抖)timer = null;// 配置请求、响应拦截器interceptor = {request: (options) => {let token = uni.getStorageSync('token');if (token) {options.header['token'] = token;}return options;},response: (res) => {if (res.isSuccess) {let code = res.data.code;switch (code) {case 200:return res.data;case 208:return this.timeoutHandler(res.data);case 401:return this.timeoutHandler(res.data);default:// return uni.showToast({//   title: '程序出现错误,正在修复',//   icon: 'none',//   duration: 2000,// });}} else {uni.showToast({title: '请求失败',icon: 'none',duration: 2000,});return Promise.reject(res);}},};constructor(defaultProps1) {if (defaultProps1) {this.defaultProps = Object.assign({}, this.defaultProps, defaultProps1);}}request(options) {let requestOptions = Object.assign({}, this.defaultProps, options);requestOptions.url = requestOptions.BaseURL + (options.url.startsWith('/') ? options.url : '/' + options.url);let interceptorRequest = this.interceptor.request(requestOptions);if (requestOptions.isLoading) {if (this.timer) {clearTimeout(this.timer);this.timer = null;}this.loadingCount++;console.log(this.loadingCount, 'this.loadingCount当前数量');if (this.loadingCount === 1) {uni.showLoading({title: '加载中',mask: true,});}}return new Promise(async (resolve, reject) => {try {uni.request({...interceptorRequest,success: (res) => {let interceptorResponse = this.interceptor.response({...res,config: interceptorRequest,isSuccess: true});resolve(interceptorResponse);},fail: (err) => {let interceptorResponse = this.interceptor.response({...err,config: interceptorRequest,isSuccess: false});reject(interceptorResponse);},complete: () => {if (requestOptions.isLoading) {this.loadingCount--;if (this.loadingCount === 0) {this.timer = setTimeout(() => {uni.hideLoading();this.timer = null;}, 400);}}},});} catch (error) {reject(error);}});}// get 请求get(options) {let requestOptions = Object.assign({}, options, {method: 'GET',});return this.request(requestOptions);}// post 请求post(options) {let requestOptions = Object.assign({}, options, {method: 'POST',});return this.request(requestOptions);}// 处理并发请求all(...promises) {return Promise.all(promises);}// 处理登录过期timeoutHandler(res) {uni.clearStorageSync();uni.navigateTo({url: '/pages/login/index',});uni.showToast({title: '登录过期,请重新登录',icon: 'none',duration: 2000,});return res;}
}// 实例化
let WxRequest = new WxRequestClass();// 用来自定义配置,想怎么搞就怎么搞
const customInstance = (options) => {return new WxRequestClass(options);
};
export {customInstance
};export default WxRequest;

五、封装优点总结

功能点说明
✅ 全局统一配置BaseURL、超时时间、请求头集中管理
✅ 自动 token 注入登录状态自动携带
✅ 登录过期处理自动跳转、提示用户
✅ 并发 loading 管理多请求只显示一个 loading
✅ 防闪烁机制连续请求无闪烁
✅ 支持无 loading 请求灵活可控
✅ 支持多实例配置方便扩展不同服务场景

http://www.dtcms.com/a/556917.html

相关文章:

  • Less-7 GET-Dump into outfile-String
  • Windows系统暂停强制更新的操作(超详细说明)
  • Leetcode 43
  • 力扣每日一题——接雨水
  • 基于AWS Lambda事件驱动架构与S3智能生命周期管理的制造数据自动化处理方案
  • 营商环境建设网站建设公司网站的必要性
  • 小网站广告投放网站做支付需要准备什么东西吗
  • 第六届“大湾区杯”粤港澳金融数学建模竞赛赛题浅析-助攻快速选题
  • 【车载Android】使用自定义插件实现多语言自动化适配
  • 学习网站建设要什么学历网站颜色表
  • C++ 分治 归并排序解决问题 力扣 315. 计算右侧小于当前元素的个数 题解 每日一题
  • Linux UdpSocket的应用
  • docker compose 创建MySQL8后在容器里备份数据到宿主机(.sql文件)的方式
  • 南昌网站外包几何图形生成网站
  • 《算法通关指南:数据结构和算法篇 --- 顺序表相关算法题》--- 询问学号,寄包柜,合并两个有序数组
  • OS_3 Memory、4 File、5 IO
  • Jenkins vs Tekton vs Arbess,CI/CD工具一文纵评
  • 如何挑选中药饮片供应商才能确保产品质量与安全?
  • 自己制作的网站如何发布素材网站都有哪些
  • 双非大学生自学鸿蒙5.0零基础入门到项目实战 -《基础篇》
  • webrtc代码走读(十四)-QOS-Jitter
  • 计算机网络经典问题透视:当路由器需要同时连接以太网和ATM网络时,需要添加什么硬件?
  • IntelliJ IDEA从安装到使用:零基础完整指南
  • 怎么做局域网asp网站做网站1天转多钱
  • Oracle常用
  • [VT-Refine] Simulation | Fine-Tuning | docker/run.sh
  • 如何修改网站域名制作自己的网站需要什么材料
  • docker快速上手笔记
  • 生成私钥公钥
  • 免费自助建站自助建站平台推广一般收多少钱