UniApp开发中通用的Request请求封装request.js

www.jswusn.com Other 2026-07-06 17:11:42 5次浏览


在HBuilderX开发工具中开发UniApp时,经常会使用到Request请求,每一个接口请求时都要写一个uin.request,频繁且重复写这个代码。

不想重复写代码就要想到怎么去完美封装 request.js。

UniApp 请求封装,包含:

1.全局请求 / 响应拦截

2.统一 Loading 管理(支持多请求自动合并)

3.统一错误提示

4.支持多个 baseURL 自由切换

5.支持 Promise 化

6.支持请求头自动携带 Token及Key名称自定义

7.全局 / 单个接口 自由设置 Content-Type、自定义请求超时
8.支持 GET / POST / PUT / DELETE / PATCH

一、 完整封装代码 utils/request.js

/**
 * * uni-app 通用网络请求封装
 * */
// 可配置多个基础URL(你可以无限加)
const BASE_URL = {
  // 开发域名
  dev: 'https://dev-api.xxx.com',
  // 测试域名
  test: 'https://test-api.xxx.com',
  // 生产域名
  prod: 'https://api.xxx.com',
  // 第三方接口域名(示例,可自行新增)
  third: 'https://other-api.xxx.com'
}

// 全局默认配置
const GLOBAL_CONFIG = {
  defaultEnv: 'dev', // 默认使用的域名key
  contentType: 'application/json', // 默认Content-Type
  timeout: 10000, // 默认超时时间 10秒
  loadingText: '加载中...', //loading显示文本
  tokenKey: 'token', //保存Token的Key
  loginPath: '/pages/login/login' //Token过期时跳转的路径
}

// ==================== 状态管理 ====================
let loadingCount = 0;
let loadingTimer = null;
// ==================== Loading 管理 ====================
const showLoading = () => {
  loadingCount++;
  loadingTimer = setTimeout(() => {
    if (loadingCount > 0) {
      uni.showLoading({
        title: GLOBAL_CONFIG.loadingText,
        mask: true
      });
    }
  }, 200);
}
const hideLoading = () => {
  loadingCount--;
  if (loadingCount <= 0) {
    loadingCount = 0;
    clearTimeout(loadingTimer);
    uni.hideLoading();
  }
}
// ==================== 跳转登录(防重复) ====================
let isRedirecting = false;
const redirectToLogin = (tokenKey, loginPath) => {
  if (isRedirecting)
    return;
  isRedirecting = true;

  uni.removeStorageSync(tokenKey);

// 延迟跳转,避免白屏
  setTimeout(() => {
    uni.navigateTo({
      url: loginPath
    });
    isRedirecting = false;
  }, 100);
}

// 统一错误提示
const showToast = (msg, duration = 1500, icon = "none") => {
  uni.showToast({
    title: msg || '请求失败',
    icon: icon,
    duration: duration
  });
}

/**
 * 核心请求方法
 * @param {object} options 请求配置
 * @param {string} options.url - 接口地址
 * @param {string} options.contentType  Content-Type的设置
 * @param {string} options.baseURL  使用哪个URL:base1/base2/base3
 * @param {string} options.skipLoading  是否跳过 loading
 * @param {string} options.skipToken  是否跳过 Token(如登录接口)
 * @param {string} options.tokenKey  保存Token的Key
 * @param {string} options.loginPath  Token过期时跳转的路径
 * @param {string} options.skipErrorToast   是否跳过错误提示
 * @param {number} options.timeout  本请求超时时间
 */
const request = (options) => {
// 拼接完整请求地址
  let baseKey = options.baseURL || GLOBAL_CONFIG.defaultEnv;
  let baseUrl = BASE_URL[baseKey];

  let url = options.url;
  //console.log(url)
  let fullUrl = url.startsWith('http') ? url : `${baseUrl}${url}`;
  //console.log(fullUrl)

  // 超时时间(单个请求可覆盖)
  let timeout = options.timeout || GLOBAL_CONFIG.timeout;
  // 是否跳过 loading
  let skipLoading = options.skipLoading || false;
  // 是否跳过 Token(如登录接口)
  let skipToken = options.skipToken || false;
  // 保存Token的Key
  let tokenKey = options.tokenKey || GLOBAL_CONFIG.tokenKey;
  // 是否跳过错误提示
  let skipErrorToast = options.skipErrorToast || false;

  // 请求拦截 ======================================
  // 1. 显示 loading
  if (!skipLoading) {
    showLoading();
  }

  //Content-Type 优先级:接口自定义 > 全局默认
  let contentType = options.contentType || GLOBAL_CONFIG.contentType
  // 可在这里加 token
  let header = {
    'Content-Type': contentType,
    ...options.header
  };
  // 从本地存储获取 Token
  let token = uni.getStorageSync(tokenKey);
  if (token) {
    header['Authorization'] = `Bearer ${token}`;
  }

  return new Promise((resolve, reject) => {
    uni.request({
      url: fullUrl,
      method: options.method || 'GET',
      data: options.data || {},
      header,
      timeout,
      success: (res) => {
        // 响应拦截 ==================================
        if (!skipLoading) {
          hideLoading();
        }
        let statusCode = res.statusCode
        let data = res.data;

        // 状态码判断(根据你后端规则改)
        // 业务逻辑状态码判断 (假设后端返回格式为 { code: 200, msg: 'success', data: {} })
        if (statusCode >= 200 && statusCode < 300) {
          resolve(data);
        } else if (statusCode === 401) {
          // Token 过期
          redirectToLogin();
          reject(data);
        } else if (statusCode === 403) {
          // 无权限
          if (!skipErrorToast) {
            showToast('无访问权限', 2000);
          }
          reject(data)
        } else if (statusCode === 404) {
          if (!skipErrorToast) {
            showToast('请求资源不存在', 2000);
          }
          reject(data);
        } else {
          if (!skipErrorToast) {
            showToast(data.msg || data.message || '请求异常', 2000);
          }

          reject(data);
        }
      },
      fail: (err) => {
        console.error('Request Fail:', err);
        if (!skipLoading) {
          hideLoading();
        }

        let message = '网络异常,请检查网络';
        if (err.errMsg) {
          if (err.errMsg.includes('timeout')) {
            message = '请求超时';
          }
        }
        if (!skipErrorToast) {
          showToast(message, 2000);
        }
        reject(err);
      }
    })
  })
}

// 导出快捷方法
export default {
  request,
  /** GET请求 */
  get: (url, data = {}, config = {}) => request({
    url,
    method: 'GET',
    data,
    ...config
  }),
  /** POST请求 */
  post: (url, data = {}, config = {}) => request({
    url,
    method: 'POST',
    data,
    ...config
  }),
  /** PUT请求 */
  put: (url, data = {}, config = {}) => request({
    url,
    method: 'PUT',
    data,
    ...config
  }),
  /** DELETE请求 */
  delete: (url, data = {}, config = {}) => request({
    url,
    method: 'DELETE',
    data,
    ...config
  }),
  /** PATCH请求 */
  patch: (url, data = {}, config = {}) => request({
    url,
    method: 'PATCH',
    data,
    ...config
  })
}


二、全局挂载(main.js)

import request from './utils/request.js'
// 全局挂载
uni.$http = request
uni.$get = request.get
uni.$post = request.post
uni.$put = request.put
uni.$delete = request.delete
uni.$patch = request.patch


三、页面使用示例(超级简单)

// GET 请求
uni.$http.get('/user/info', {}, {
  timeout: 30000 // 请求超时时间
}).then(res => {
  console.log(res)
})
uni.$get('/user/info').then(res => {
  console.log(res)
})

// POST 请求
uni.$http.post('/user/login', {
  username: 'admin',
  password: '123456'
}, {
  baseURL: 'test', // 测试环境  
  skipToken: false, // 登录接口不需要 token  
  timeout: 30000 // 请求超时时间
}).then(res => {
  console.log(res)
})
uni.$post('/user/login', {
  username: 'admin',
  password: '123456'
}, {
  baseURL: 'test', // 测试环境  
  skipToken: false, // 登录接口不需要 token  
  timeout: 30000 // 请求超时时间
}).then(res => {
  console.log(res)
})


以上基本实现接口请求的统一封装了。

技术分享

苏南名片

  • 联系人:技术-小吴
  • 电话:152-1887-1916
  • 邮箱:message@jswusn.com
  • 地址:江苏省苏州市相城区

热门文章

Copyright © 2018-2026 jswusn.com 版权所有

技术支持:苏州网站建设  苏ICP备18036849号