'# vue学习---基于vue2中的axios
一、背景与问题
在Vue2项目开发中,前后端分离架构已成为主流。前端需要通过HTTP请求与后端API进行数据交互,而axios作为主流的HTTP客户端库,其高效、灵活的特性使得它成为Vue项目中最常用的网络请求解决方案。
传统fetch API存在诸多限制:无法直接拦截请求/响应、缺乏自动转换JSON数据、缺少请求/响应拦截器等。而axios通过封装XMLHttpRequest,提供了更强大的功能,包括:
- 自动转换JSON数据
- 支持Promise API
- 提供拦截器机制
- 支持请求/响应的拦截处理
- 自动处理HTTP错误状态码
在实际开发中,常见的问题包括:
- 跨域问题(CORS)
- 请求超时处理
- 错误处理不完善
- 缺乏统一的请求封装
- 安全性隐患
二、基本原理
1. axios底层实现
axios基于XMLHttpRequest进行封装,通过创建Axios类实现核心功能。其核心原理包括:
- 建立请求配置对象
- 创建XMLHttpRequest实例
- 设置请求头、方法、超时等参数
- 监听
onload和onerror事件 - 处理响应数据和错误
// axios核心封装(简化版)
function createAxios(config) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(config.method, config.url, true);
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText));
} else {
reject({ status: xhr.status, data: xhr.responseText });
}
};
xhr.onerror = function() {
reject({ status: 0, data: 'Network error' });
};
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(config.data));
});
}2. 请求/响应拦截器机制
axios通过axios.interceptors提供拦截器功能,分为请求拦截器和响应拦截器:
// 请求拦截器示例
axios.interceptors.request.use(config => {
// 添加请求头
config.headers.Authorization = 'Bearer ' + getToken();
// 添加请求时间戳
config.headers['X-Request-Time'] = Date.now();
return config;
}, error => {
// 请求错误处理
console.error('Request error:', error);
return Promise.reject(error);
});
// 响应拦截器示例
axios.interceptors.response.use(response => {
// 处理响应数据
if (response.data.code === 200) {
return response.data.data;
} else {
throw new Error(response.data.message);
}
}, error => {
// 响应错误处理
console.error('Response error:', error);
return Promise.reject(error);
});3. 并发请求处理
通过axios.all和axios.spread处理多个并发请求:
axios.all([
axios.get('/api/users'),
axios.get('/api/posts')
]).then(axios.spread((users, posts) => {
console.log('Users:', users);
console.log('Posts:', posts);
}));三、环境准备
1. 项目依赖
在Vue2项目中需要安装axios:
npm install axios --save2. 项目结构建议
src/
├── api/ # API接口封装
├── utils/ # 工具函数
├── services/ # 服务模块
├── main.js # 入口文件
├── App.vue # 根组件
└── axios.js # axios配置文件四、核心实现
1. 创建axios实例
// src/axios.js
import axios from 'axios';
// 创建axios实例
const service = axios.create({
baseURL: process.env.VUE_APP_API_URL, // 从.env文件读取API地址
timeout: 10000, // 超时时间
withCredentials: true, // 允许跨域请求携带cookie
});
// 添加请求拦截器
service.interceptors.request.use(config => {
// 从本地存储获取token
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}, error => {
return Promise.reject(error);
});
// 添加响应拦截器
service.interceptors.response.use(response => {
if (response.data.code === 200) {
return response.data.data;
} else {
// 统一错误处理
const message = response.data.message || 'Server error';
return Promise.reject(new Error(message));
}
}, error => {
if (error.response) {
// 接收到响应但状态码不在2xx范围
console.error('Server error:', error.response.status);
} else if (error.request) {
// 没有收到响应
console.error('No response received');
} else {
// 请求初始化错误
console.error('Request error:', error.message);
}
return Promise.reject(error);
});
export default service;2. 封装请求方法
// src/utils/request.js
import service from './axios';
// 封装get请求
export function get(url, params) {
return service.get(url, { params });
}
// 封装post请求
export function post(url, data) {
return service.post(url, data);
}
// 封装put请求
export function put(url, data) {
return service.put(url, data);
}
// 封装delete请求
export function del(url, params) {
return service.delete(url, { params });
}3. 带拦截器的请求示例
// src/components/UserList.vue
<template>
<div>
<ul>
<li v-for="user in users" :key="user.id">{{ user.name }}</li>
</ul>
</div>
</template>
<script>
import { get } from '@/utils/request';
export default {
data() {
return {
users: []
};
},
mounted() {
this.fetchUsers();
},
methods: {
async fetchUsers() {
try {
const res = await get('/api/users');
this.users = res;
} catch (error) {
console.error('Failed to fetch users:', error);
this.users = [];
}
}
}
};
</script>五、完整案例
1. 用户管理系统案例
项目结构
src/
├── api/
│ └── user.js
├── services/
│ └── user.js
├── utils/
│ └── request.js
├── axios.js
├── main.js
├── App.vue
└── UserList.vueAPI接口定义
// src/api/user.js
export const list = '/api/users';
export const detail = '/api/users/:id';
export const create = '/api/users';
export const update = '/api/users/:id';
export const remove = '/api/users/:id';服务模块
// src/services/user.js
import { get, post, put, del } from '@/utils/request';
import { list, detail, create, update, remove } from '@/api/user';
export async function fetchUsers() {
return get(list);
}
export async function fetchUser(id) {
return get(detail.replace(':id', id));
}
export async function createUser(data) {
return post(create, data);
}
export async function updateUser(id, data) {
return put(update.replace(':id', id), data);
}
export async function deleteUser(id) {
return del(remove.replace(':id', id));
}组件使用示例
// src/components/UserList.vue
<template>
<div>
<div>用户列表</div>
<ul>
<li v-for="user in users" :key="user.id">
{{ user.name }} - {{ user.email }}
<button @click="deleteUser(user.id)">删除</button>
</li>
</ul>
</div>
</template>
<script>
import { fetchUsers, deleteUser } from '@/services/user';
export default {
data() {
return {
users: []
};
},
mounted() {
this.fetchUsers();
},
methods: {
async fetchUsers() {
try {
const res = await fetchUsers();
this.users = res;
} catch (error) {
console.error('Failed to fetch users:', error);
this.users = [];
}
},
async deleteUser(id) {
try {
await deleteUser(id);
this.users = this.users.filter(user => user.id !== id);
alert('删除成功');
} catch (error) {
console.error('Delete error:', error);
alert('删除失败');
}
}
}
};
</script>六、源码解析
1. axios源码核心结构
axios源码主要包含以下几个核心模块:
createInstance:创建axios实例createInterceptor:创建拦截器createRequest:创建请求createResponse:创建响应createError:创建错误对象
// 简化版源码结构
function createInstance(config) {
const instance = {
defaults: {},
interceptors: {
request: {
handlers: [],
use: (fulfilled, rejected) => {
// 添加请求拦截器
}
},
response: {
handlers: [],
use: (fulfilled, rejected) => {
// 添加响应拦截器
}
}
},
request: function(config) {
// 创建请求
}
};
return instance;
}2. 请求拦截器执行顺序
// 请求拦截器执行流程
requestConfig
.then(interceptors.request.handlers[0])
.then(interceptors.request.handlers[1])
.then(...)
.then(config => {
// 发起请求
})七、进阶使用
1. 自定义拦截器
// 自定义请求拦截器
service.interceptors.request.use(config => {
// 添加请求头
config.headers['X-App-Version'] = '1.0.0';
// 添加请求时间戳
config.headers['X-Request-Time'] = Date.now();
// 添加请求参数
config.params = {
...config.params,
timestamp: Date.now()
};
return config;
}, error => {
return Promise.reject(error);
});2. 并发请求优化
// 使用axios.all处理并发请求
axios.all([
axios.get('/api/users'),
axios.get('/api/posts')
]).then(axios.spread((users, posts) => {
console.log('Users:', users);
console.log('Posts:', posts);
}));3. 安全增强措施
// 添加安全请求头
config.headers['X-Content-Type-Options'] = 'nosniff';
config.headers['X-Frame-Options'] = 'SAMEORIGIN';
config.headers['X-XSS-Protection'] = '1; mode=block';八、性能与工程实践
1. 性能优化策略
(1) 缓存机制
// 使用本地缓存
const cache = new Map();
function getWithCache(url, params) {
const key = `${url}?${new URLSearchParams(params).toString()}`;
if (cache.has(key)) {
return Promise.resolve(cache.get(key));
}
return service.get(url, { params }).then(data => {
cache.set(key, data);
return data;
});
}(2) 并发控制
// 使用并发控制
const pendingRequests = new Map();
function getWithConcurrency(url, params) {
const key = `${url}?${new URLSearchParams(params).toString()}`;
if (pendingRequests.has(key)) {
return pendingRequests.get(key);
}
const promise = service.get(url, { params }).then(data => {
pendingRequests.delete(key);
return data;
}).catch(error => {
pendingRequests.delete(key);
throw error;
});
pendingRequests.set(key, promise);
return promise;
}(3) 超时控制
// 设置超时时间
const timeout = 5000; // 5秒
service.interceptors.request.use(config => {
config.timeout = timeout;
return config;
});2. 异常处理规范
// 统一错误处理
service.interceptors.response.use(response => {
if (response.data.code === 200) {
return response.data.data;
} else {
const message = response.data.message || 'Server error';
return Promise.reject(new Error(message));
}
}, error => {
if (error.response) {
// 接收到响应但状态码不在2xx范围
console.error(`Server error: ${error.response.status}`);
} else if (error.request) {
// 没有收到响应
console.error('No response received');
} else {
// 请求初始化错误
console.error('Request error:', error.message);
}
return Promise.reject(error);
});3. 安全性实践
(1) 跨域解决方案
// 后端CORS配置示例(Node.js)
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});(2) 请求签名机制
// 请求签名生成
function generateSign(params, secret) {
const sortedParams = Object.keys(params).sort().map(key =>
`${key}=${params[key]}`).join('&');
return CryptoJS.HmacSHA256(sortedParams, secret).toString();
}九、常见问题与踩坑
1. 跨域问题(CORS)
错误现象:浏览器控制台显示No 'Access-Control-Allow-Origin' header is present on the requested resource
解决方法:
- 后端配置CORS
- 使用代理服务器(开发环境)
- 使用
axios的withCredentials配置
// 开发环境代理配置(vue.config.js)
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
};2. 错误处理不完善
错误现象:网络异常时页面崩溃
改进方法:添加全局错误处理
// 全局错误处理
window.onerror = function(message, source, lineno, colno, error) {
console.error('Global error:', message, error);
return true;
};3. 超时处理不当
错误现象:请求卡死导致页面卡顿
改进方法:设置合理的超时时间,并处理超时错误
// 设置超时时间
service.defaults.timeout = 5000;
// 处理超时错误
service.interceptors.response.use(response => {
// 处理响应数据
return response.data;
}, error => {
if (error.code === 'ECONNABORTED') {
console.error('Request timeout');
}
return Promise.reject(error);
});4. 安全性隐患
风险点:明文传输敏感信息
解决方案:
- 使用HTTPS
- 对敏感数据进行加密
- 添加请求签名验证
// 请求签名验证(服务端)
function verifySign(params, secret) {
const expectedSign = generateSign(params, secret);
const actualSign = params.sign;
return expectedSign === actualSign;
}十、最佳实践
1. 接口封装规范
- 所有API接口统一归档
- 使用枚举定义接口路径
- 接口参数校验
- 接口版本控制
2. 通用错误处理
- 统一错误类型
- 错误日志记录
- 错误码映射
- 错误提示规范
// 错误码映射
const errorMap = {
400: '请求参数错误',
401: '未授权',
403: '禁止访问',
404: '资源不存在',
500: '服务器错误'
};3. 安全性实践
- 必要的请求头验证
- 敏感数据加密传输
- 请求签名验证
- CORS安全配置
- 防止CSRF攻击
4. 性能优化方案
- 响应式数据缓存
- 并发请求控制
- 压缩数据传输
- 异步加载优化
- 资源预加载
十一、总结
在Vue2项目中使用axios进行网络请求,需要综合考虑多个方面:
- 核心原理:理解axios基于XMLHttpRequest的封装机制,掌握拦截器、并发请求等核心功能
- 实际应用:通过封装通用请求方法,统一接口处理,提高代码复用性
- 性能优化:通过缓存、并发控制、超时设置等手段提升性能
- 安全性:注意跨域、数据加密、请求签名等安全风险
- 错误处理:完善错误处理机制,避免页面崩溃
- 工程实践:遵循规范的代码组织方式,保持代码可维护性
在实际开发中,建议:
- 使用统一的axios配置文件
- 封装通用请求方法
- 配置拦截器处理错误
- 遵循安全实践规范
- 定期进行性能优化
需要注意的是,虽然axios功能强大,但在以下场景可能不是最佳选择:
- 需要复杂请求重试机制
- 需要支持 WebSocket
- 需要处理大量二进制数据
- 需要更细粒度的控制
对于这些场景,可以考虑使用更专业的库如axios-multipart处理文件上传,axios-sockjs处理WebSocket,或使用fetch配合fetch-mock进行单元测试。