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

uni-app 网络请求封装实战:打造高效、可维护的HTTP请求框架

前言

在uniapp开发中,网络请求是每个应用都必不可少的功能模块。一个优秀的网络请求封装不仅能提高开发效率,还能增强代码的可维护性和可扩展性。本文将基于实际项目经验,详细介绍如何封装一个高效、可维护的Uniapp网络请求框架,并结合Bing图片API的调用示例,展示其完整使用流程。

 

一、网络请求封装的核心目标

  1. 统一管理:集中管理请求配置、拦截器等
  2. 复用性:减少重复代码,提高开发效率
  3. 可扩展性:方便添加新功能如日志记录、错误处理等
  4. 可维护性:清晰的代码结构,便于团队协作

二、封装架构设计

1.项目结构规划

项目根目录下新建文件夹common,在其目录下新建 api 文件夹以及 vmeitime-http 文件夹 ,如下

common/
├── api/               # API接口管理
│   └── index.js       # 接口统一出口
└── vmeitime-http/     # HTTP核心封装├── http.js        # 请求核心实现└── interface.js   # 基础请求方法

然后再项目根目录下的main.js下引用

//api
import api from '@/common/api/index.js'
Vue.prototype.$api = api

2.核心组件解析

(1) 基础请求层 (interface.js)

这是整个框架的基石,实现了:

  • 基础请求方法(GET/POST/PUT/DELETE)
  • 请求超时处理
  • 请求ID生成(便于日志追踪)
  • 基础配置管理
/*** 通用uni-app网络请求* 基于 Promise 对象实现更简单的 request 使用方式,支持请求和响应拦截*/
export default {config: {baseUrl: "",header: {'Content-Type': 'application/x-www-form-urlencoded'},data: {},method: "POST",dataType: "json",/* 如设为json,会对返回的数据做一次 JSON.parse */responseType: "text",timeout: 15000, // 全局超时时间 15 秒fail() {},complete() {}},interceptor: {request: null,response: null},request(options) {if (!options) {options = {}}// 1. 生成唯一请求ID并挂载到optionsconst requestId = generateRequestId();options.requestId = requestId; // 给请求配置添加IDoptions.baseUrl = options.baseUrl || this.config.baseUrloptions.dataType = options.dataType || this.config.dataTypeoptions.url = options.baseUrl + options.urloptions.data = options.data || {}options.method = options.method || this.config.methodoptions.timeout = options.timeout || this.config.timeout; // 使用配置的超时时间return new Promise((resolve, reject) => {let _config = nulllet timeoutHandle = null;// 超时处理if (options.timeout) {timeoutHandle = setTimeout(() => {reject({errMsg: "request:fail timeout",timeout: true,requestId: requestId // 超时错误也携带ID,方便定位});}, options.timeout);}options.complete = (response) => {// 无论成功失败,都清除超时计时器clearTimeout(timeoutHandle);_config = Object.assign({}, this.config, options);response.config = _config; // 给响应挂载配置,供拦截器使用// 执行响应拦截器if (this.interceptor.response) {const interceptedResponse = this.interceptor.response(response);if (interceptedResponse !== undefined) {response = interceptedResponse;}}// 统一的响应日志记录_reslog(response)if (response.statusCode === 200) { //成功resolve(response);} else {reject(response)}}// 失败回调(网络错误等)options.fail = (error) => {clearTimeout(timeoutHandle);error.requestId = requestId; // 网络错误携带IDconsole.error(`【网络请求失败】ID: ${requestId}`, error);uni.showToast({title: error.timeout ? "请求超时" : "网络连接失败,请检查网络",icon: "none"});reject(error);};// 执行请求拦截器_config = Object.assign({}, this.config, options);if (this.interceptor.request) {const interceptedConfig = this.interceptor.request(_config);if (interceptedConfig !== undefined) {_config = interceptedConfig;}}// 统一的请求日志记录_reqlog(_config)uni.request(_config);});},get(url, data, options) {if (!options) {options = {}}options.url = urloptions.data = dataoptions.method = 'GET'return this.request(options)},post(url, data, options) {if (!options) {options = {}}options.url = urloptions.data = dataoptions.method = 'POST'return this.request(options)},put(url, data, options) {if (!options) {options = {}}options.url = urloptions.data = dataoptions.method = 'PUT'return this.request(options)},delete(url, data, options) {if (!options) {options = {}}options.url = urloptions.data = dataoptions.method = 'DELETE'return this.request(options)}
}/*** 请求接口日志记录*/function _reqlog(req) {//开发环境日志打印if (process.env.NODE_ENV === 'development') {const reqId = req.requestId || '未知ID'; // 读取请求IDconst reqUrl = req.url || '未知URL'; // 读取完整URLconsole.log(`【${reqId}】 地址:${reqUrl}`);if (req.data) {console.log("【" + (req.requestId || '未知ID') + "】 请求参数:", req.data);}}
}/*** 响应接口日志记录*/
function _reslog(res) {if (!res) {console.error("【日志错误】响应对象为空");return;}const requestId = res.config?.requestId || '未知请求ID';const url = res.config?.url || '未知URL';const statusCode = res.statusCode || '未知状态码';console.log(`【${requestId}】 接口: ${url} | 业务状态码: ${statusCode}`);// 打印响应数据if (res.data) {console.log(`【${requestId}】 响应数据:`, res.data);}// 错误处理逻辑switch (statusCode) {case "401":console.error(`【${requestId}】 未授权错误`);break;case "404":console.error(`【${requestId}】 接口不存在`);break;case "500":console.error(`【${requestId}】 服务器错误`);break;default:}
}
/*** 生成唯一请求ID(时间戳+随机数,避免重复)*/
function generateRequestId() {const timestamp = Date.now().toString(36); // 时间戳转36进制(缩短长度)const randomStr = Math.random().toString(36).slice(2, 8); // 6位随机字符串return `${timestamp}-${randomStr}`; // 格式:时间戳-随机串(如:1h8z2x-9k7a3b)
}

(2) 请求增强层 (http.js)

在基础层之上添加:

  • 全局拦截器(请求/响应)
  • 公共参数处理
  • 错误统一处理
  • 默认配置初始化
import uniRequest from './interface'uniRequest.config.baseUrl = "https://cn.bing.com"
uniRequest.config.header = {'Content-Type': 'application/x-www-form-urlencoded'
}// 公共参数(所有请求都会携带)
const commonParams = {};//设置请求前拦截器
uniRequest.interceptor.request = (config) => {//添加通用参数let token = uni.getStorageSync('token');if (token) {config.header["X-Token"] = token;}// 合并公共参数和业务参数const mergedData = {...commonParams, // 公共参数...config.data // 业务参数(会覆盖公共参数)};config.data = mergedData;return config;
}
//设置请求结束后拦截器
uniRequest.interceptor.response = (response) => {return response;
}export default uniRequest

(3) API接口层 (api/index.js)

import http from '@/common/vmeitime-http/http.js'const $api = {//查看必应图片byImageList(data) {return http.request({url: '/HPImageArchive.aspx?format=js&idx=0&n=1',method: 'GET',data,})}
}
export default $api

三、使用示例:调用Bing图片API

<template><view><!-- 显示加载状态 --><view v-if="loading" class="loading">{{ tooltips.loading }}</view><!-- 显示Bing图片 --><image v-else :src="fullImageUrl" mode="widthFix" class="bing-image"></image></view>
</template><script>export default {data() {return {loading: true, // 加载状态fullImageUrl: "", // 拼接后的完整图片URL};},onLoad() {uni.showLoading({title: '请稍后',mask: true // 防止用户重复操作});this.fetchBingImage().finally(() => {this.loading = false; // 确保 loading 被关闭uni.hideLoading(); // 无论成功失败都关闭加载框});},methods: {fetchBingImage() {return this.$api.byImageList().then(res => {console.log("请求成功:", res);console.log('images 数组:', res.data.images);// 2. 提取 images 数组的第一项const imageData = res.data.images[0];console.log('images图片:', imageData.url);// 3. 拼接完整URL(http://cn.bing.com + url)this.fullImageUrl = `https://cn.bing.com${imageData.url}`;console.log('images图片地址:', this.fullImageUrl);}).catch(err => {// 此时的err只可能是:HTTP错误、解析异常、业务失败(result≠0000)console.error("请求失败:", err);uni.showToast({title: err.error || err.timeout ? '请求超时' : '请求失败',icon: 'none',duration: 2000});return Promise.reject(err); // 继续抛出,供上层处理});},},};
</script><style>.loading {font-size: 16px;color: #999;text-align: center;margin-top: 50px;}.bing-image {width: 100%;height: auto;}
</style>

四、高级功能实现

1. 日志系统

// 请求日志
function _reqlog(req) {if (process.env.NODE_ENV === 'development') {console.log(`【${req.requestId}】 地址:${req.url}`);if (req.data) {console.log("请求参数:", req.data);}}
}// 响应日志
function _reslog(res) {const requestId = res.config?.requestId || '未知请求ID';console.log(`【${requestId}】 接口: ${res.config?.url} | 状态码: ${res.statusCode}`);// 错误状态码处理switch (res.statusCode) {case 401: console.error(`未授权错误`); break;case 404: console.error(`接口不存在`); break;case 500: console.error(`服务器错误`); break;}
}

2. 请求ID生成算法

function generateRequestId() {const timestamp = Date.now().toString(36); // 时间戳转36进制const randomStr = Math.random().toString(36).slice(2, 8); // 6位随机字符串return `${timestamp}-${randomStr}`; // 格式:时间戳-随机串
}


文章转载自:

http://eC1h6prr.hmdyL.cn
http://kCoG7K5v.hmdyL.cn
http://sXOUIM42.hmdyL.cn
http://VGnAIvAT.hmdyL.cn
http://ZPpOoG3X.hmdyL.cn
http://280QuKC7.hmdyL.cn
http://xSMD27yP.hmdyL.cn
http://o59OpEkP.hmdyL.cn
http://H9BOHYo5.hmdyL.cn
http://HZXyBamt.hmdyL.cn
http://luOeJiqP.hmdyL.cn
http://PrmcdzFq.hmdyL.cn
http://Or2ib8Aj.hmdyL.cn
http://9T4Xb32w.hmdyL.cn
http://O8zTNYFQ.hmdyL.cn
http://j4KOkcnU.hmdyL.cn
http://axsYsZYl.hmdyL.cn
http://OURGUcJv.hmdyL.cn
http://de4cZ5FK.hmdyL.cn
http://kXog1b37.hmdyL.cn
http://OK5qmCGT.hmdyL.cn
http://VNhHi2pC.hmdyL.cn
http://d8skScph.hmdyL.cn
http://671rt8dV.hmdyL.cn
http://1VRdfax2.hmdyL.cn
http://cXf4Bhef.hmdyL.cn
http://252f2B5g.hmdyL.cn
http://spM0HhUT.hmdyL.cn
http://qNVpDpjb.hmdyL.cn
http://lgqrwdfU.hmdyL.cn
http://www.dtcms.com/a/378783.html

相关文章:

  • AppTest邀请测试测试流程
  • C#地方门户网站 基于NET6.0、Admin.NET,uniapp,vue3,elementplus开源的地方门户网站项目
  • 苹果上架全流程详解,iOS 应用发布步骤、App Store 上架流程、uni-app 打包上传与审核要点完整指南
  • PyTorch之张量创建与运算
  • Python爬虫实战:研究GUI 可视化界面,构建电商数据采集和分析系统
  • 对浏览器事件机制的理解
  • JavaWeb05
  • 浅聊一下ThreadLocal
  • 部署大模型的极简笔记
  • linux面试题记录
  • 深度解码OpenAI的2025野心:Codex重生与GPT-5 APIKey获取调用示例
  • 文献阅读笔记:脉冲神经网络最新文献合集-IV
  • STM32学习路线开启篇:芯片简介与课程简介
  • 第七章 ELK Stack高级应用与集成
  • 认识跨平台UI框架Flutter和MAUI区别,如何选。
  • 9.11-QT-QT的基本使用
  • 线程安全相关的注解
  • [超表面论文快讯-242] PR-微波超四元数涡旋阵列洛书加权锁定成像加密-江南大学王继成、上海科技大学王雄团队
  • 质量特性工程
  • 性能测试-jmeter10-分布式测试
  • Java中方法重写与重载的区别
  • 【代码随想录算法训练营——Day9】字符串——151.翻转字符串里的单词、卡码网:55.右旋转字符串、28.实现 strStr()、459.重复的子字符串
  • 少儿舞蹈小程序(13)作品播放量累加及点赞
  • 【AI知识点】模型训练优化之——混合精度训练
  • 华为考试:HCIE数通考试难度分析
  • OpenHarmony app通过U盘升级配置
  • mobx-miniprogram小程序的数据传输
  • rdKafka驾驭手册:从青铜到王者的异步消息屠龙术
  • Ubuntu\Linux环境中驱动版本配置cudaToolKit
  • 法规变更后研发文档更新不及时该怎么办