uniapp如何封装uni.request 全局使用

发布于:2025-07-30 ⋅ 阅读:(14) ⋅ 点赞:(0)

在 UniApp 中封装 uni.request 实现全局使用,可通过以下方案实现 统一配置、拦截器管理、错误处理和全局调用。以下是完整实现步骤:


一、基础封装方案(推荐)

1. 创建 http.js 请求封装模块
// utils/http.js

// 基础配置
const BASE_URL = 'https://api.example.com'
const TIMEOUT = 15000

// 请求队列(用于防止重复提交)
const pendingRequests = new Map()

// 生成请求唯一标识
const generateReqKey = (config) => {
  return `${config.url}&${config.method}`
}

// 核心请求函数
const request = (options) => {
  // 合并配置
  const config = {
    url: options.url,
    data: options.data || {},
    method: options.method || 'GET',
    header: {
      'Content-Type': 'application/json',
      'X-Token': uni.getStorageSync('token') || '', // 自动携带token
      ...(options.header || {}),
    },
    timeout: TIMEOUT,
    ...options,
  }

  // 处理完整URL
  if (!config.url.startsWith('http')) {
    config.url = BASE_URL + config.url
  }

  // 防止重复请求拦截
  const reqKey = generateReqKey(config)
  if (pendingRequests.has(reqKey)) {
    return Promise.reject({ errMsg: '重复请求已取消' })
  }
  pendingRequests.set(reqKey, config)

  return new Promise((resolve, reject) => {
    uni.request({
      ...config,
      success: (res) => {
        // 请求完成后移除队列
        pendingRequests.delete(reqKey)
        
        // 全局响应拦截器
        if (res.statusCode === 200) {
          // 业务状态码处理(示例)
          if (res.data.code === 401) {
            uni.navigateTo({ url: '/pages/login/login' })
            return reject(res.data)
          }
          resolve(res.data)
        } else {
          // HTTP状态码错误处理
          reject({ 
            code: res.statusCode,
            message: `请求失败: ${res.errMsg}`
          })
        }
      },
      fail: (err) => {
        pendingRequests.delete(reqKey)
        // 网络错误统一处理
        reject({
          code: -1,
          message: '网络连接失败,请检查网络'
        })
      },
      complete: () => {
        // 可添加全局Loading关闭逻辑
        uni.hideLoading()
      }
    })
  })
}

// 封装常用方法
const http = {
  get: (url, params = {}, options = {}) => {
    return request({
      url,
      data: params,
      method: 'GET',
      ...options
    })
  },
  
  post: (url, data = {}, options = {}) => {
    return request({
      url,
      data,
      method: 'POST',
      ...options
    })
  },

  put: (url, data = {}, options = {}) => {
    // 其他方法类似封装
  }
}

export default http
2. 全局挂载(main.js)
// main.js
import Vue from 'vue'
import App from './App'
import http from '@/utils/http'

// 挂载到Vue原型
Vue.prototype.$http = http

// 挂载到全局uni对象
uni.$http = http

// 如需使用this.$http调用
Vue.use({
  install(Vue) {
    Vue.config.globalProperties.$http = http
  }
})
3. 页面中使用
// 在.vue文件中
export default {
  methods: {
    async fetchData() {
      try {
        // GET请求
        const res1 = await this.$http.get('/user', { id: 123 })
        
        // POST请求
        const res2 = await uni.$http.post('/login', {
          username: 'admin',
          password: '123456'
        })
      } catch (err) {
        uni.showToast({ title: err.message, icon: 'none' })
      }
    }
  }
}

二、高级功能扩展

1. 添加拦截器系统
// 在http.js中添加
const interceptors = {
  request: [],
  response: []
}

// 注册拦截器
http.addRequestInterceptor = (interceptor) => {
  interceptors.request.push(interceptor)
}

http.addResponseInterceptor = (interceptor) => {
  interceptors.response.push(interceptor)
}

// 修改request函数
const request = async (options) => {
  // ...原有代码...
  
  // 执行请求拦截器
  for (const interceptor of interceptors.request) {
    config = await interceptor(config) || config
  }
  
  return new Promise((resolve, reject) => {
    uni.request({
      ...config,
      success: async (res) => {
        // 执行响应拦截器
        let response = res
        for (const interceptor of interceptors.response) {
          response = await interceptor(response) || response
        }
        // ...后续处理...
      }
    })
  })
}
2. 使用示例(Token刷新)
// 在main.js中注册拦截器
http.addRequestInterceptor(config => {
  if (!config.url.includes('/refresh-token')) {
    const token = uni.getStorageSync('token')
    if (token) config.header['X-Token'] = token
  }
  return config
})

http.addResponseInterceptor(async res => {
  if (res.data.code === 401) {
    // Token过期自动刷新
    const newToken = await http.post('/refresh-token', {
      refreshToken: uni.getStorageSync('refreshToken')
    })
    uni.setStorageSync('token', newToken)
    // 重新发送原请求
    return http(res.config)
  }
  return res
})

三、多端适配要点

  1. H5跨域处理

    // 开发环境代理配置 (vue.config.js)
    devServer: {
      proxy: {
        '/api': {
          target: 'https://api.example.com',
          changeOrigin: true,
          pathRewrite: { '^/api': '' }
        }
      }
    }
  2. 小程序域名白名单

    • 需在 manifest.json 配置合法域名

    "mp-weixin": {
      "appid": "xxx",
      "setting": {
        "urlCheck": false
      },
      "permission": {
        "scope.userLocation": {
          "desc": "需要获取您的位置信息"
        }
      },
      "requiredPrivateInfos": ["getLocation"]
    }
  3. App端证书校验

    // 仅iOS需要处理
    if (uni.getSystemInfoSync().platform === 'ios') {
      config.sslVerify = false // 关闭SSL验证(仅测试环境)
    }

四、最佳实践建议

  1. 全局Loading控制

    // 请求计数
    let requestCount = 0
    
    http.addRequestInterceptor(config => {
      if (!config.hideLoading) {
        requestCount === 0 && uni.showLoading({ title: '加载中', mask: true })
        requestCount++
      }
      return config
    })
    
    http.addResponseInterceptor(response => {
      if (!response.config.hideLoading) {
        requestCount--
        requestCount === 0 && uni.hideLoading()
      }
      return response
    })
  2. 请求重试机制

    const retryRequest = (config, retry = 3) => {
      return request(config).catch(err => {
        return retry > 0 
          ? retryRequest(config, retry - 1) 
          : Promise.reject(err)
      })
    }
  3. TypeScript支持

    // http.d.ts
    declare module '@vue/runtime-core' {
      interface ComponentCustomProperties {
        $http: {
          get<T = any>(url: string, params?: object): Promise<T>;
          post<T = any>(url: string, data?: object): Promise<T>;
        }
      }
    }

封装优势

  • 统一管理所有网络请求逻辑

  • 自动处理 token 和权限认证

  • 支持多端差异化配置

  • 提供拦截器实现业务解耦

  • 内置防重复提交和错误重试机制


网站公告

今日签到

点亮在社区的每一天
去签到