告别重复代码在Uniapp项目中用Vue3TS优雅封装uni.request附完整拦截器配置在开发中大型跨端应用时每个页面都充斥着零散的请求代码不仅难以维护还容易产生类型安全问题。本文将带你从零构建一个类型安全、可复用的请求层彻底告别重复劳动。1. 为什么需要封装uni.requestuni.request作为Uniapp的基础网络请求API直接使用会面临几个典型问题配置重复每个请求都需要重复设置baseURL、超时时间、headers等类型缺失响应数据没有类型提示后期维护困难逻辑分散错误处理、loading控制等逻辑分散在各处扩展困难新增全局功能如自动重试需要修改所有请求点对比Web开发中常用的Axiosuni.request缺少实例化机制和拦截器体系。通过封装我们可以实现// 封装后的理想调用方式 const { data } await api.getUser[](/users, { params: { page: 1 } })2. 核心封装方案设计2.1 基础拦截器配置Uniapp提供了uni.addInterceptorAPI实现请求拦截这是封装的基础const BASE_URL import.meta.env.VITE_API_BASEURL const httpInterceptor { invoke(options: UniApp.RequestOptions) { if (!options.url.startsWith(http)) { options.url BASE_URL options.url } options.timeout 10000 options.header { ...options.header, Content-Type: application/json, X-Client-Type: uniapp } const token uni.getStorageSync(token) if (token) { options.header.Authorization Bearer ${token} } } } uni.addInterceptor(request, httpInterceptor)2.2 实现Promise封装为保持与Axios相似的开发体验我们封装返回Promise的请求方法interface ResponseDataT any { code: number message: string data: T } function requestT(options: UniApp.RequestOptions) { return new PromiseResponseDataT((resolve, reject) { uni.request({ ...options, success: (res) { if (res.statusCode 200 res.statusCode 300) { resolve(res.data as ResponseDataT) } else { handleError(res, reject) } }, fail: (err) { handleError(err, reject) } }) }) }3. 高级功能实现3.1 类型安全增强通过泛型实现完整的类型提示export const api { getT(url: string, config?: OmitUniApp.RequestOptions, url | method) { return requestT({ ...config, url, method: GET }) }, postT(url: string, data?: any, config?: OmitUniApp.RequestOptions, url | method | data) { return requestT({ ...config, url, data, method: POST }) } // 其他方法... } // 使用时获得完整类型提示 interface User { id: number name: string avatar: string } const { data } await api.getUser[](/users)3.2 全局错误处理统一处理常见错误场景function handleError(error: any, reject: (reason?: any) void) { // 401跳转登录 if (error.statusCode 401) { uni.navigateTo({ url: /pages/login/index }) return } // 网络错误 if (error.errMsg?.includes(fail)) { uni.showToast({ title: 网络连接失败, icon: none }) return } // 业务错误 const message error.data?.message || 请求失败 uni.showToast({ title: message, icon: none }) reject(error) }3.3 请求取消与重试实现类似Axios的CancelToken机制const pendingRequests new Mapstring, UniApp.RequestTask() function generateRequestKey(config: UniApp.RequestOptions) { return ${config.method}-${config.url} } function addPendingRequest(config: UniApp.RequestOptions) { const key generateRequestKey(config) const task uni.request({ ...config, success: () pendingRequests.delete(key), fail: () pendingRequests.delete(key) }) pendingRequests.set(key, task) } function removePendingRequest(config: UniApp.RequestOptions) { const key generateRequestKey(config) const task pendingRequests.get(key) if (task) { task.abort() pendingRequests.delete(key) } }4. 完整实现与最佳实践4.1 完整封装代码// http.ts import type { UniApp } from dcloudio/uni-app declare module dcloudio/uni-app { interface RequestOptions { retry?: number loading?: boolean } } const BASE_URL import.meta.env.VITE_API_BASEURL const DEFAULT_TIMEOUT 10000 interface ResponseDataT any { code: number message: string data: T } class Http { private pendingRequests new Mapstring, UniApp.RequestTask() constructor() { this.setupInterceptor() } private generateRequestKey(config: UniApp.RequestOptions) { return ${config.method}-${config.url} } private setupInterceptor() { uni.addInterceptor(request, { invoke: (options) { if (!options.url.startsWith(http)) { options.url BASE_URL options.url } options.timeout options.timeout || DEFAULT_TIMEOUT options.header { Content-Type: application/json, ...options.header } const token uni.getStorageSync(token) if (token) { options.header.Authorization Bearer ${token} } if (options.loading ! false) { uni.showLoading({ title: 加载中..., mask: true }) } }, success: (res) { if (res.loading ! false) { uni.hideLoading() } }, fail: (err) { if (err.loading ! false) { uni.hideLoading() } } }) } public requestT(options: UniApp.RequestOptions) { return new PromiseResponseDataT((resolve, reject) { const retryCount options.retry || 0 const doRequest (retry 0) { const task uni.request({ ...options, success: (res) { if (res.statusCode 200 res.statusCode 300) { resolve(res.data as ResponseDataT) } else if (retry retryCount) { doRequest(retry 1) } else { this.handleError(res, reject) } }, fail: (err) { if (retry retryCount) { doRequest(retry 1) } else { this.handleError(err, reject) } } }) this.pendingRequests.set( this.generateRequestKey(options), task as UniApp.RequestTask ) } doRequest() }) } private handleError(error: any, reject: (reason?: any) void) { // 错误处理逻辑... } public getT(url: string, config?: OmitUniApp.RequestOptions, url | method) { return this.requestT({ ...config, url, method: GET }) } // 其他快捷方法... } export const http new Http()4.2 使用建议目录结构/src /api user.ts # 用户相关接口 product.ts # 产品相关接口 http.ts # 封装的请求库接口模块化// api/user.ts import { http } from ./http export const getUserInfo (id: number) { return http.getUser(/users/${id}) } export const updateUser (data: UserUpdateDTO) { return http.putvoid(/users, data) }类型定义// types/user.d.ts interface User { id: number name: string avatar: string } interface UserUpdateDTO { name?: string avatar?: string }在实际项目中这种封装方式可以减少30%以上的重复代码量同时显著提升代码的可维护性和类型安全性。