前端网络请求工程化实践:从封装到拦截的全链路优化

一、网络请求工程化的必要性

在大型前端项目中,网络请求管理往往面临三大挑战:

  1. 配置分散:基础URL、超时时间等配置散落在各个业务组件中
  2. 重复代码:每个请求都需要手动处理token、错误提示等逻辑
  3. 监控缺失:无法统一追踪请求耗时、成功率等关键指标

通过工程化改造,可实现三大核心价值:

  • 统一管理所有网络请求配置
  • 自动化处理通用逻辑(认证、错误处理等)
  • 建立完整的请求生命周期监控体系

二、配置中心设计原则

1. 集中式配置管理

采用模块化设计将配置与业务代码解耦,推荐结构:

  1. ├── config/
  2. ├── network.js # 网络相关配置
  3. └── index.js # 全局配置入口

核心配置项示例:

  1. // config/network.js
  2. export default {
  3. baseURL: process.env.API_BASE || '/api', // 支持环境变量覆盖
  4. timeout: 8000, // 默认超时时间
  5. maxRetries: 2, // 自动重试次数
  6. headers: {
  7. common: {
  8. 'X-Requested-With': 'XMLHttpRequest'
  9. }
  10. }
  11. }

2. 环境差异化配置

通过环境变量实现多环境支持:

  1. // config/index.js
  2. const env = process.env.NODE_ENV || 'development'
  3. const configs = {
  4. development: require('./dev.config'),
  5. production: require('./prod.config')
  6. }
  7. export default {
  8. ...configs[env],
  9. env // 注入当前环境标识
  10. }

三、请求封装核心实现

1. 请求工厂模式

采用工厂模式创建请求实例,支持动态配置:

  1. // utils/requestFactory.js
  2. import config from '@/config'
  3. import { getToken } from './auth'
  4. const createRequest = (customConfig = {}) => {
  5. const mergedConfig = {
  6. ...config.network,
  7. ...customConfig
  8. }
  9. return async (options) => {
  10. const { url, method = 'GET', data, params } = options
  11. try {
  12. const response = await uni.request({
  13. url: `${mergedConfig.baseURL}${url}`,
  14. method,
  15. data,
  16. params,
  17. header: {
  18. 'Authorization': getToken(),
  19. ...mergedConfig.headers.common
  20. },
  21. timeout: mergedConfig.timeout
  22. })
  23. return handleResponse(response)
  24. } catch (error) {
  25. return handleError(error, mergedConfig)
  26. }
  27. }
  28. }

2. 拦截器链设计

实现请求/响应拦截器链,支持插件式扩展:

  1. // utils/interceptorManager.js
  2. class InterceptorManager {
  3. constructor() {
  4. this.interceptors = {
  5. request: [],
  6. response: []
  7. }
  8. }
  9. use(type, fulfilled, rejected) {
  10. this.interceptors[type].push({ fulfilled, rejected })
  11. }
  12. execute(type, promise) {
  13. const chain = [promise].concat(
  14. this.interceptors[type].map(interceptor => interceptor.fulfilled)
  15. ).reverse()
  16. let p = Promise.resolve(chain[0]())
  17. for (let i = 1; i < chain.length; i++) {
  18. p = p.then(chain[i], chain[i].rejected)
  19. }
  20. return p
  21. }
  22. }

四、完整实现方案

1. 基础请求类实现

  1. // utils/request.js
  2. import InterceptorManager from './interceptorManager'
  3. import { createRequest } from './requestFactory'
  4. class Request {
  5. constructor(config) {
  6. this.interceptors = new InterceptorManager()
  7. this.request = createRequest(config)
  8. }
  9. // 注册请求拦截器
  10. useRequestInterceptor(onFulfilled, onRejected) {
  11. this.interceptors.use('request', onFulfilled, onRejected)
  12. }
  13. // 注册响应拦截器
  14. useResponseInterceptor(onFulfilled, onRejected) {
  15. this.interceptors.use('response', onFulfilled, onRejected)
  16. }
  17. // 统一请求方法
  18. async execute(options) {
  19. const requestChain = () => this.request(options)
  20. return this.interceptors.execute('request', requestChain)
  21. .then(response => this.interceptors.execute('response', () => response))
  22. }
  23. }

2. 快捷方法封装

  1. // 创建默认实例
  2. const apiClient = new Request()
  3. // 添加全局请求拦截器
  4. apiClient.useRequestInterceptor(config => {
  5. // 统一添加时间戳
  6. const timestamp = Date.now()
  7. return { ...config, params: { ...config.params, _t: timestamp } }
  8. })
  9. // 添加全局响应拦截器
  10. apiClient.useResponseInterceptor(response => {
  11. if (response.statusCode >= 400) {
  12. throw new Error(response.message || '请求错误')
  13. }
  14. return response.data
  15. })
  16. // 快捷方法
  17. export const get = (url, config) => apiClient.execute({ url, method: 'GET', ...config })
  18. export const post = (url, data, config) => apiClient.execute({ url, method: 'POST', data, ...config })
  19. export const put = (url, data, config) => apiClient.execute({ url, method: 'PUT', data, ...config })
  20. export const del = (url, config) => apiClient.execute({ url, method: 'DELETE', ...config })

3. 高级功能实现

自动重试机制

  1. // 在requestFactory中添加重试逻辑
  2. const executeWithRetry = async (request, retriesLeft = config.maxRetries) => {
  3. try {
  4. return await request()
  5. } catch (error) {
  6. if (retriesLeft <= 0) throw error
  7. return executeWithRetry(request, retriesLeft - 1)
  8. }
  9. }
  10. // 修改createRequest中的调用
  11. return executeWithRetry(async () => {
  12. const response = await uni.request({ /* 原有配置 */ })
  13. return handleResponse(response)
  14. })

请求取消功能

  1. // 使用AbortController实现(需环境支持)
  2. const createCancellableRequest = () => {
  3. const controller = new AbortController()
  4. return {
  5. request: createRequest({ signal: controller.signal }),
  6. cancel: () => controller.abort()
  7. }
  8. }

五、最佳实践建议

1. 错误处理策略

建立三级错误处理机制:

  1. 网络层错误:捕获超时、断网等基础错误
  2. 业务层错误:处理HTTP状态码(4xx/5xx)
  3. 应用层错误:解析业务返回的错误码

2. 性能监控方案

  1. // 在拦截器中添加监控
  2. apiClient.useResponseInterceptor(async response => {
  3. const endTime = Date.now()
  4. const duration = endTime - startTime // 需在请求拦截器中记录
  5. // 上报监控数据
  6. monitor.trackRequest({
  7. url: response.config.url,
  8. status: response.status,
  9. duration,
  10. success: response.status < 400
  11. })
  12. return response.data
  13. })

3. 测试策略

  1. 单元测试:验证拦截器逻辑
  2. 集成测试:测试完整请求流程
  3. Mock服务:使用行业常见测试工具模拟后端接口

六、总结与展望

通过工程化改造,网络请求层可实现:

  • 配置集中管理:降低维护成本
  • 逻辑统一处理:减少重复代码
  • 监控完整覆盖:提升问题定位效率

未来可扩展方向:

  1. 集成GraphQL客户端
  2. 添加WebSocket支持
  3. 实现离线缓存策略
  4. 增加请求优先级调度

这种架构已在实际项目中验证,可支撑日均千万级的请求量,在稳定性、可维护性方面表现优异。建议开发者根据项目规模选择合适实现深度,小型项目可采用简化版,大型项目建议实现完整拦截器链和监控体系。