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
通过封装,我们可以实现:
- 统一接口:屏蔽平台差异
- 拦截器机制:在请求前后统一处理
- 错误重试:网络不稳定时的自动重试
- 状态管理:全局loading控制
- 安全处理:自动添加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' });
}
}
}
}六、源码解析
请求拦截器:
- 自动添加token
- 添加时间戳防止缓存
- 设置Content-Type
- 日志记录
响应拦截器:
- 统一处理200状态码的返回数据
- 网络错误提示
- 返回结构标准化
重试机制:
- 网络错误时自动重试
- 限制最大重试次数
- 重试间隔时间
七、进阶使用
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. 性能优化
缓存策略:
// 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; }); });压缩数据:
// utils/request.js service.interceptors.request.use(config => { if (config.method === 'post') { config.data = compressData(config.data); } return config; });连接复用:
// utils/request.js service.defaults.headers.common['Connection'] = 'keep-alive';
2. 安全风险
Token泄露:
- 建议使用刷新token机制
- 使用HTTPS加密传输
- 避免在URL中传递敏感信息
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);
}
}十、最佳实践
- 统一接口:所有请求都通过封装后的
request函数发起 - 分模块管理:按功能模块划分请求接口(如user、product等)
- 错误分类:区分网络错误、业务错误、权限错误
- 日志记录:记录关键请求的详细信息用于调试
- 性能监控:记录请求耗时,进行性能分析
- 安全加固:使用HTTPS,敏感数据加密,CSRF防护
十一、总结
uniapp封装AJAX请求是一个涉及多方面技术的工程实践。通过合理封装,可以解决重复代码、平台差异、错误处理等问题,提高开发效率和代码质量。在实际开发中,需要根据项目需求选择合适的封装策略,同时注意性能优化和安全防护。对于复杂业务场景,建议使用更高级的封装方案,如结合状态管理库和中间件模式,实现更完善的网络请求管理。
评论已关闭