普通视图

发现新文章,点击刷新页面。
昨天以前首页

uni.request 二次封装

2026年1月30日 10:30

基于uni.request api进行二次封装

核心功能

  • 响应内容格式和请求参数格式类型定义
  • 请求拦截器与响应拦截器配置
  • 设置请求头和params参数处理
  • 加载提示与自定义提示文本
  • 错误统一处理
  • 接口缓存
  • 取消请求功能
  • 失败自动重试机制
  • 并发请求控制

核心代码

// 响应内容格式
export interface InResult<T> {
  code: number | string
  message: string
  success: boolean
# data: T
}
// 请求参数格式
interface RequestOptions {
  baseURL?: string
  url: string
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE'
  data?: Record<string, any>
  params?: Record<string, any>
  header?: Record<string, string>
  loading?: boolean // 是否显示加载中提示
  loadingText?: string // 加载中提示文本
  removeToken?: boolean // 是否移除token
  cache?: boolean // 是否缓存响应结果
  returnResponse?: boolean // 直接返回响应
}

// 设置请求头
function setRequestHeaders(url: string) {
  const LOGIN_URL = '/auth/oauth2/token'
  if (url.includes(LOGIN_URL)) {
    return { Authorization: `Basic Z2VveHNwYWNlOmdlb3hzcGFjZQ==` }
  }
  const token = uni.getStorageSync('token')
  if (token) {
    return { Authorization: `Bearer ${token}` }
  }
  return {} as Record<string, string>
}
// 请求和响应拦截器
function requestAndResponseInterceptor() {
  uni.addInterceptor('request', {
    // 调用前置拦截器
    invoke(options: RequestOptions) {
      if (options.loading) {
        uni.showLoading({
          title: options.loadingText || '加载中...',
          mask: true,
        })
      }

      options.header = {
        ...options.header,
        ...setRequestHeaders(options.url),
      }

      // 移除token
      if (options.removeToken) {
        delete options.header.Authorization
      }

      // 处理params 参数
      if (options.params) {
        const urlPrams: string[] = []
        Object.keys(options.params).forEach((key) => {
          urlPrams.push(`${key}=${options.params![key]}`)
        })
        if (options.url.includes('?')) {
          options.url += urlPrams.join('&')
        }
        else {
          options.url += `?${urlPrams.join('&')}`
        }
      }
      return options
    },

    // 调用后置拦截器
    success(res) {
      return res
    },
    fail(err) {
      uni.showToast({
        title: '网络请求失败',
        icon: 'none',
      })
      return Promise.reject(err)
    },

    complete(option) {
      console.log('option.errMsg', option.errMsg)
      setTimeout(() => {
        uni.hideLoading()
      }, 15000)
    },
  })
}

// 调用请求拦截器和响应拦截器
requestAndResponseInterceptor()

const cacheMap = new Map<string, any>()
// 封装网络请求
export async function request(options: RequestOptions): Promise<any> {
  const { baseURL, url, header = {}, cache, returnResponse } = options
  // 合并配置
  const config = {
    ...options,
    url: url.startsWith('http') ? options.url : baseURL + options.url,
    header: {
      'Content-Type': 'application/json',
      ...header, // 允许自定义header
    },
    timeout: 10000, // 超时时间(ms)
  }

  if (cache) {
    if (cacheMap.has(url)) {
      return cacheMap.get(url)
    }
  }

  try {
    const response = await uni.request(config)

    if (options.loading) {
      uni.hideLoading()
    }
    // 响应拦截器
    if (response.statusCode === 200) {
      const data = returnResponse ? response : response.data
      if (cache) {
        cacheMap.set(url, data)
      }

      // @ts-expect-error 判断异常
      if (response.data.code !== '20000') {
        // @ts-expect-error 判断异常
        toast(response.data.msg, {
          icon: 'fail',
        })
      }

      return data
    }
    if (response.statusCode === 401) {
      toast('登录已过期,请重新登录!', {}, () => {
        uni.redirectTo({
          url: '/user/login/index',
        })
      })
    }
    else {
      toast('系统服务异常!')
    }
  }
  catch (error) {
    return Promise.reject(error)
  }
}

toast 封装代码

export function toast(title: string, options?: { duration?: number, icon?: 'success' | 'loading' | 'error' | 'none' | 'fail' | 'exception', mask?: boolean }, callback?: () => void) {
  const { mask = true, duration = 1000, icon = 'none' } = options || {}

  if (title && title.length > 14) {  // 当作字符长度>14时使用showModal展示
    uni.showModal({
      content: title,
      showCancel: false,
      success() {
        if (callback && typeof callback === 'function') {
          const timer = setTimeout(() => {
            callback()
            clearTimeout(timer)
          }, duration)
        }
      },
    })
  }
  else {
    uni.showToast({
      title,
      message: title,
      icon,
      mask,
      duration,
      success() {
        if (callback && typeof callback === 'function') {
          const timer = setTimeout(() => {
            callback()
            clearTimeout(timer)
          }, duration)
        }
      },
    })
  }
}

使用示例

import { type InResult, request } from '@/utils/request'

export interface IUser {
    id: string,
    name: string,
    age?: number
}

export function getUserList(data: any): Promise<InResult<{ records: Array<IUser>, total: number }>> {
  return request({
    baseURL,
    url: '/user/page',
    method: 'POST',
    params: data,
    loading: true,
  })
}

微信小程序开发中碰到在组件中监听app.globalData 中值得变化 处理相对应的数据

作者 葉_
2026年1月30日 09:41

1.方法一 如果是简单数据 可以用Object.defineProperty 进行监听处理

Object.defineProperty 是 JavaScript 中的一个方法,用于直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。这个方法允许你精确地控制属性的特性,比如是否可写、是否可枚举、是否可配置等。

以下是 Object.defineProperty 方法的基本用法和参数说明
Object.defineProperty(obj, prop, descriptor)
  • obj:要在其上定义或修改属性的对象。
  • prop:要定义或修改的属性的名称,可以是一个字符串或 Symbol
  • descriptor:一个描述符对象,包含以下属性:
  • configurable:布尔值,表示属性是否可以被删除或重新配置。
  • enumerable:布尔值,表示属性是否可以在 for...in 循环或 Object.keys() 方法中被枚举。
  • writable:布尔值,表示属性值是否可以被改变。
  • value:属性的数据值,对于数据属性是必需的。
  • get:获取属性的访问器函数,对于存取器属性是必需的。
  • set:设置属性的访问器函数,对于存取器属性是必需的。
小程序中用法
//在app.js中配置全局函数
setWatcher(targetKey, callback) {
    const globalData = this.globalData;
    // 如果该属性尚未被监听,则为其定义访问器
    if (!globalData.hasOwnProperty(`_${targetKey}`)) {
      let val = globalData[targetKey];
      
      Object.defineProperty(globalData, targetKey, {
        configurable: true,
        enumerable: true,
        set(value) {
          val = value;
          // 调用所有注册的回调函数
          if (globalData._watchers && globalData._watchers[targetKey]) {
            globalData._watchers[targetKey].forEach((cb) => cb(value, val));
          }
        },
        get() {
          return val;
        }
      });
      
      // 初始化 watchers 存储对象
      if (!globalData._watchers) {
        globalData._watchers = {};
      }
      // 初始化该属性的回调函数数组
      if (!globalData._watchers[targetKey]) {
        globalData._watchers[targetKey] = [];
      }
    }
    
    // 将回调函数添加到该属性的监听列表中
    globalData._watchers[targetKey].push(callback);
  },
  
  
  //在相对应的组件中lifetimes中调用
   apps.setWatcher('role', (newValue:any, oldValue:any) => {
        console.log('role 已更新:', newValue);
        this.setData({
          userInfo: newValue
        });
      });
效果

971e286ff8719e8093d95b562db847b3.png

5408e68e0d633a90b79bbdd24fbc520b.png

2.方法二

例:

user:{ roles:{ role:0 } }需要修改role的值

可以用Proxy 对globalData进行代理

JavaScript的Proxy是一种对象代理机制,它可以在对象和函数之间添加一个中间层,从而实现对对象和函数的拦截和控制。Proxy可以用于拦截对象的读写、函数的调用、属性的枚举等操作,并在拦截时执行自定义的操作。使用Proxy可以实现各种高级功能,例如数据绑定、事件监听、缓存等。

以下是 Proxy 方法的基本用法和参数说明
let proxy = new Proxy(target, handler);

其中,target是要被代理的目标对象,handler是一个对象,用于定义拦截目标对象的各种操作的行为。handler对象可以包含以下方法:

  • get(target, prop, receiver):拦截对象属性的读取操作。
  • set(target, prop, value, receiver):拦截对象属性的写入操作。
  • apply(target, thisArg, args):拦截函数的调用操作。
  • construct(target, args, newTarget):拦截new操作符的调用操作。
  • has(target, prop):拦截in操作符的调用操作。
  • deleteProperty(target, prop):拦截delete操作符的调用操作。
  • defineProperty(target, prop, descriptor):拦截Object.defineProperty()方法的调用操作。
  • getOwnPropertyDescriptor(target, prop):拦截Object.getOwnPropertyDescriptor()方法的调用操作。
  • getPrototypeOf(target):拦截Object.getPrototypeOf()方法的调用操作。
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf()方法的调用操作。
  • isExtensible(target):拦截Object.isExtensible()方法的调用操作。
  • preventExtensions(target):拦截Object.preventExtensions()方法的调用操作。
  • ownKeys(target):拦截Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()、Object.keys()等方法的调用操作。
小程序中用法
//app.js中
// 代理全局数据
  initGlobalDataProxy() {
    const self = this;
    
    // 递归创建代理
    const createProxy = (data, path) => {
      if (typeof data !== 'object' || data === null) {
        return data;
      }
      
      return new Proxy(data, {
        get(target, prop, receiver) {
          const value = Reflect.get(target, prop, receiver);
          // 如果是对象则继续代理
          if (typeof value === 'object' && value !== null) {
            return createProxy(value, path ? `${path}.${String(prop)}` : String(prop));
          }
          return value;
        },
        set(target, prop, value, receiver) {
          const currentPath = path ? `${path}.${String(prop)}` : String(prop);
          const oldValue = Reflect.get(target, prop, receiver);
          const result = Reflect.set(target, prop, value, receiver);

          if (oldValue !== value) {
            console.log('数据变化:', currentPath, '新值:', value);
            
            // 1. 触发精确路径监听
            if (self?.globalDataListeners && self.globalDataListeners[currentPath]) {
              self.globalDataListeners[currentPath].forEach((callback) => {
                callback(value, currentPath);
              });
            }
            
            // 2. 触发父级路径监听(例如修改 user.role 时,如果监听了 user,也会触发)
            let parentPath = path;
            while (parentPath) {
              if (self?.globalDataListeners && self.globalDataListeners[parentPath]) {
                const parentValue = parentPath.split('.').reduce((obj, key) => obj[key], self.globalData);
                self.globalDataListeners[parentPath].forEach((callback) => {
                  callback(parentValue, parentPath);
                });
              }
              const lastDot = parentPath.lastIndexOf('.');
              parentPath = lastDot !== -1 ? parentPath.substring(0, lastDot) : '';
            }
          }
          return result;
        }
      });
    };

    this.globalData = createProxy(this.globalData);
  },
  // 添加监听器
  addGlobalDataListener(prop:any, callback:any) {
    if (!this.globalDataListeners) {
      this.globalDataListeners = {};
    }
    if (!this.globalDataListeners[prop]) {
      this.globalDataListeners[prop] = [];
    }
    this.globalDataListeners[prop].push(callback);
  }
  
  
  
  //在相对应的组件中lifetimes中调用
  apps.initGlobalDataProxy();
    
  // 监听 userInfo 变化
  apps.addGlobalDataListener('user', (newValue:any) => {
  console.log("更改值了",newValue.roles.role)
  });
 
效果

72185a009e9cb02f783c400ac40a8a30.png

就可以动态更改组件的需求了 当然这只是其中几种方法 可能有的小伙伴会有更好的方法 欢迎来讨论

❌
❌