uniapp封装AJAX请求

'# uniapp封装AJAX请求

一、背景与问题

在uniapp开发中,频繁的网络请求是常态。但直接使用uni.request存在诸多问题:

  • 重复代码:每个请求都需要重复编写配置
  • 缺乏统一管理:无法集中处理错误、超时、token刷新等
  • 平台差异:微信/支付宝/H5等不同平台的网络策略差异
  • 状态管理:缺少全局的loading状态控制
  • 安全性缺失:未对敏感数据进行加密处理

传统做法是直接调用uni.request,但随着项目规模扩大,这种做法会导致代码冗余和维护困难。本文将深入探讨如何构建一个健壮的AJAX封装方案。

二、基本原理

uniapp的网络请求体系基于uni.request接口,其底层调用不同平台的原生API:

  • 微信小程序:wx.request
  • 支付宝小程序:my.request
  • H5:fetch或XMLHttpRequest
  • App:plus.request

通过封装,我们可以实现:

  1. 统一接口:屏蔽平台差异
  2. 拦截器机制:在请求前后统一处理
  3. 错误重试:网络不稳定时的自动重试
  4. 状态管理:全局loading控制
  5. 安全处理:自动添加token、加密数据

三、环境准备

# 创建uniapp项目
uni create my-ajax-demo
cd my-ajax-demo

安装依赖(如使用axios):

npm install axios

四、核心实现

1. 基础封装

// utils/request.js
import axios from 'axios';

const service = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000
});

// 请求拦截器
service.interceptors.request.use(config => {
  // 添加token
  const token = uni.getStorageSync('token');
  if (token) {
    config.headers['Authorization'] = `Bearer ${token}`;
  }
  
  // 添加请求体
  if (config.method === 'post') {
    config.data = {
      ...config.data,
      timestamp: Date.now()
    };
  }
  
  return config;
}, error => {
  return Promise.reject(error);
});

// 响应拦截器
service.interceptors.response.use(response => {
  // 处理响应数据
  if (response.data.code === 200) {
    return response.data.data;
  } else {
    uni.showToast({ title: '服务器错误', icon: 'none' });
    return Promise.reject(response.data.message);
  }
}, error => {
  // 处理网络错误
  if (error.response) {
    uni.showToast({ title: '网络错误', icon: 'none' });
    return Promise.reject(error.response.data);
  }
  return Promise.reject(error);
});

export default service;

关键点解释:

  • 使用axios创建统一的请求实例
  • 在请求拦截器中添加token和时间戳
  • 响应拦截器统一处理200状态码的返回数据
  • 对网络错误进行统一提示

2. 拦截器扩展

// utils/request.js
// 增加请求拦截器
service.interceptors.request.use((config) => {
  // 增加请求日志
  console.log(`[请求] ${config.method} ${config.url}`);
  
  // 添加请求头
  config.headers['Content-Type'] = 'application/json';
  
  // 自动转换URL
  config.url = `${config.baseURL}${config.url}`;
  
  return config;
}, (error) => {
  return Promise.reject(error);
});

3. 错误重试机制

// utils/request.js
// 增加重试逻辑
function retryRequest(config, retries = 3) {
  return new Promise((resolve, reject) => {
    const attempt = () => {
      service(config)
        .then(resolve)
        .catch((err) => {
          if (retries > 0 && err.code === 'ERR_NETWORK') {
            retries--;
            setTimeout(() => attempt(), 1000);
          } else {
            reject(err);
          }
        });
    };
    attempt();
  });
}

五、完整案例

1. 登录流程

// pages/login/login.vue
export default {
  methods: {
    async login() {
      const { username, password } = this;
      
      try {
        const res = await retryRequest({
          url: '/api/login',
          method: 'POST',
          data: { username, password }
        });
        
        uni.setStorageSync('token', res.token);
        uni.showToast({ title: '登录成功', icon: 'success' });
      } catch (err) {
        uni.showToast({ title: '登录失败', icon: 'none' });
      }
    }
  }
}

2. 获取用户信息

// pages/user/user.vue
export default {
  async onLoad() {
    try {
      const data = await retryRequest({
        url: '/api/user',
        method: 'GET'
      });
      
      this.userInfo = data;
    } catch (err) {
      uni.showToast({ title: '获取用户信息失败', icon: 'none' });
    }
  }
}

3. 文件上传

// pages/upload/upload.vue
export default {
  methods: {
    async uploadFile() {
      try {
        const res = await retryRequest({
          url: '/api/upload',
          method: 'POST',
          data: new FormData()
        });
        
        uni.showToast({ title: '上传成功', icon: 'success' });
      } catch (err) {
        uni.showToast({ title: '上传失败', icon: 'none' });
      }
    }
  }
}

六、源码解析

  1. 请求拦截器:

    • 自动添加token
    • 添加时间戳防止缓存
    • 设置Content-Type
    • 日志记录
  2. 响应拦截器:

    • 统一处理200状态码的返回数据
    • 网络错误提示
    • 返回结构标准化
  3. 重试机制:

    • 网络错误时自动重试
    • 限制最大重试次数
    • 重试间隔时间

七、进阶使用

1. 路由拦截

// utils/request.js
service.interceptors.request.use(config => {
  if (config.url === '/api/login') return config;
  
  const token = uni.getStorageSync('token');
  if (!token) {
    uni.showModal({
      title: '提示',
      content: '需要登录才能访问',
      confirmText: '登录',
      success: () => {
        uni.navigateTo({ url: '/pages/login/login' });
      }
    });
    return Promise.reject();
  }
  
  return config;
});

2. 加密处理

// utils/request.js
const CryptoJS = require('crypto-js');

service.interceptors.request.use(config => {
  if (config.method === 'post') {
    const encryptedData = CryptoJS.AES.encrypt(
      JSON.stringify(config.data), 
      'secret-key'
    ).toString();
    
    config.data = {
      encryptedData,
      algorithm: 'AES'
    };
  }
  
  return config;
});

3. 自定义配置

// utils/request.js
const service = axios.create({
  baseURL: process.env.NODE_ENV === 'development' 
    ? 'https://dev-api.example.com'
    : 'https://prod-api.example.com',
  timeout: 10000,
  withCredentials: true
});

八、性能与工程实践

1. 性能优化

  1. 缓存策略:

    // utils/request.js
    const cache = new Map();
    
    service.interceptors.request.use(config => {
      const key = `${config.method}:${config.url}`;
      if (cache.has(key)) {
        return Promise.resolve(cache.get(key));
      }
      return service(config).then(res => {
        cache.set(key, res);
        return res;
      });
    });
  2. 压缩数据:

    // utils/request.js
    service.interceptors.request.use(config => {
      if (config.method === 'post') {
        config.data = compressData(config.data);
      }
      return config;
    });
  3. 连接复用:

    // utils/request.js
    service.defaults.headers.common['Connection'] = 'keep-alive';

2. 安全风险

  1. Token泄露:

    • 建议使用刷新token机制
    • 使用HTTPS加密传输
    • 避免在URL中传递敏感信息
  2. CSRF防护:

    // utils/request.js
    service.interceptors.request.use(config => {
      const csrfToken = uni.getStorageSync('csrfToken');
      if (csrfToken) {
        config.headers['X-CSRF-Token'] = csrfToken;
      }
      return config;
    });

九、常见问题与踩坑

1. 常见错误

问题原因解决方案
请求失败未配置baseURL检查配置文件
token失效未刷新token增加token刷新逻辑
网络超时超时时间过短调整timeout参数
跨域问题未配置CORS配置服务器CORS策略
401错误token无效增加token验证逻辑

2. 典型错误示例

// 错误示例:未处理异常
uni.request({
  url: 'https://api.example.com/data',
  success: (res) => {
    console.log(res.data);
  }
});

问题:未处理网络错误和异常情况
改进:

// 正确示例:使用封装后的请求
import request from '@/utils/request';

async function fetchData() {
  try {
    const data = await request({
      url: '/api/data'
    });
    console.log(data);
  } catch (err) {
    console.error(err);
  }
}

十、最佳实践

  1. 统一接口:所有请求都通过封装后的request函数发起
  2. 分模块管理:按功能模块划分请求接口(如user、product等)
  3. 错误分类:区分网络错误、业务错误、权限错误
  4. 日志记录:记录关键请求的详细信息用于调试
  5. 性能监控:记录请求耗时,进行性能分析
  6. 安全加固:使用HTTPS,敏感数据加密,CSRF防护

十一、总结

uniapp封装AJAX请求是一个涉及多方面技术的工程实践。通过合理封装,可以解决重复代码、平台差异、错误处理等问题,提高开发效率和代码质量。在实际开发中,需要根据项目需求选择合适的封装策略,同时注意性能优化和安全防护。对于复杂业务场景,建议使用更高级的封装方案,如结合状态管理库和中间件模式,实现更完善的网络请求管理。

最后修改于:2026年09月25日 15:39

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日