深度分析Vue基础教程之Axios创建实例
兄弟们,姐妹们,摸鱼划水的各位程序员们,今天咱们来聊一个在Vue项目中几乎必用,但很多人却“用而不知其所以然”的神器——Axios实例创建。
你是不是经常在代码里看到这样的场景?
// 在每个.vue文件里,都是这样的“老三样”
axios.get('https://api.myawesomeapp.com/v1/users?page=1&limit=10', {
headers: {
'Authorization': 'Bearer your_token_here'
}
})
.then(...)
axios.post('https://api.myawesomeapp.com/v1/posts', data, {
headers: {
'Authorization': 'Bearer your_token_here'
}
})
.then(...)
发现痛点了吗?第一,那个又臭又长的基地址 https://api.myawesomeapp.com/v1 像牛皮癣一样粘得到处都是,万一后端大哥哪天心情好想换个域名,你就等着全局搜索替换到天亮吧。第二,那个Authorization头,每个请求都要手动塞进去,不优雅,且容易漏。
这感觉就像,你想吃个泡面,却每次都要从种小麦开始。太原始了!太辛苦了!是时候进化了!
第一部分:为啥要创建实例?—— 从“打零工”到“正式工”的飞跃
你可以把默认的 axios 对象理解成一个“打零工”的。啥活都干,但身上没啥固定的标签。你今天让他去A公司(apiA.com)取个文件,明天让他去B公司(apiB.com)送个快递,每次都得把完整的地址和通行证(headers)告诉他。
而 创建Axios实例,就相当于你为公司里的某个特定部门(比如用户管理部),雇佣了一个“正式工”。
这个“正式工”天生就自带一些属性和技能:
- 他的工位是固定的:基地址(
baseURL)已经写进了他的劳动合同。 - 他拥有公司门禁卡:通用的请求头(
headers)在他入职时就配发了。 - 他甚至还懂点“读心术”:请求和响应拦截器,就像他的职业习惯,能在办事前后自动完成一些操作。
这样一来,当你需要这个部门办事时,你只需要对这个“正式工”说:“嘿,去给我把用户列表拿过来(/users)。” 他就能自动拼好完整的地址,带上门禁卡,完美地完成任务。
这么做的好处,用脚趾头想想都知道:
- DRY原则(Don‘t Repeat Yourself):告别重复代码,告别“复制粘贴”地狱。
- 维护性飙升:配置在一处修改,全局生效。后端换地址?改一下实例的
baseURL就行,稳如老狗。 - 职责清晰:可以为不同的后端API(比如主API、文件上传API)创建不同的实例,井水不犯河水。
- 便于封装:拦截器等高级功能有了统一的“挂载点”,代码结构更清晰。
第二部分:手把手教你“创造”一个Axios实例
理论吹得再响,不如代码来得实在。上才艺!
1. 安装与引入(准备工作)
首先,确保你的项目里已经安装了axios。
npm install axios
# 或
yarn add axios
然后,我们通常不会在组件里直接创建实例,而是单独建立一个文件来“工厂化生产”我们的axios“正式工”。比如 src/utils/request.js。
2. 基础创建:打造你的第一个“专属员工”
// src/utils/request.js
import axios from 'axios';
// 开始“创造”一个Axios实例,给它起名叫 `apiClient`
const apiClient = axios.create({
// 它的固定工位:我们的后端API基地址
baseURL: 'https://api.myawesomeapp.com/v1',
// 它的超时耐心,超过5秒没回应就撂挑子
timeout: 5000,
// 它自带的身份标识(请求头)
headers: {
'Content-Type': 'application/json',
// 这里可以先放一些通用的头,比如API密钥(非敏感)
// 'X-API-KEY': 'your-general-api-key'
}
});
// 把这个打造好的“员工”导出,供其他部门(组件)使用
export default apiClient;
现在,在Vue组件里,我们就可以愉快地使用了:
<script>
// 1. 引入我们定制好的“员工”,而不是那个“零工”axios
import apiClient from '@/utils/request';
export default {
data() {
return {
users: []
};
},
async created() {
try {
// 2. 使用它!只需要关心具体的接口路径
const response = await apiClient.get('/users');
this.users = response.data;
} catch (error) {
console.error('获取用户列表失败:', error);
}
}
};
</script>
看看,代码是不是瞬间清爽了?我们再也不需要写完整的URL了!
第三部分:注入灵魂——拦截器黑科技
如果说baseURL是给了实例一个身体,那**拦截器(Interceptors)**就是它的灵魂。它能让你在请求发出前和响应返回后,为所欲为(不是)。
1. 请求拦截器:像个贴心小秘书
想象一下,每次你的“员工”出门办事前,都自动检查一下有没有带令牌(token)。
// 在 src/utils/request.js 里,接着上面的代码写
// 请求拦截器:在请求发出之前做些事情
apiClient.interceptors.request.use(
(config) => {
// config 是本次请求的配置对象,我们可以修改它
// 假设我们把用户token存在了Vuex或者localStorage
const token = localStorage.getItem('user-token');
if (token) {
// 如果存在token,就自动添加到请求头里
config.headers.Authorization = `Bearer ${token}`;
}
console.log('请求即将发出:', config);
return config; // 必须返回config
},
(error) => {
// 对请求错误做些什么(比如配置错误,网络断开等)
return Promise.reject(error);
}
);
这样,我们就不用在每个请求里手动写 headers: { ‘Authorization’: ... } 了。拦截器自动帮我们搞定!
2. 响应拦截器:像个严格的质量检查员
当“员工”办完事回来,我们可以先让他把结果给“质检员”看看。
// 响应拦截器:在响应返回之后,then/catch之前做些事情
apiClient.interceptors.response.use(
(response) => {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据直接进行处理,比如我们只关心data里的数据
console.log('请求成功响应:', response);
return response.data; // 这里直接返回了response.data,后续then里拿到就是这个!
},
(error) => {
// 超出 2xx 范围的状态码都会触发该函数。
console.error('请求出错:', error.response);
// 统一错误处理
if (error.response.status === 401) {
// 如果是401未授权,可能是token过期,跳转到登录页
alert('登录已过期,请重新登录!');
router.push('/login');
} else if (error.response.status >= 500) {
// 服务器错误
alert('服务器开小差了,请稍后再试!');
}
// 将错误继续抛给具体的请求处理逻辑
return Promise.reject(error);
}
);
加了响应拦截器后,组件里的代码可以进一步简化:
<script>
import apiClient from '@/utils/request';
export default {
async created() {
try {
// 因为响应拦截器直接返回了 response.data,所以这里拿到就是数据本身!
const userData = await apiClient.get('/users');
this.users = userData; // 直接赋值,无需再 .data
} catch (error) {
// 通用的错误(如401)已被拦截器处理,这里可以处理特定于这个请求的错误
console.error('这个请求的特殊错误处理:', error);
}
}
};
</script>
第四部分:完整示例——打造一个企业级Vue项目请求中心
让我们把所有代码整合到一起,形成一个生产环境可用的、功能完善的请求模块。
src/utils/request.js
import axios from 'axios';
import { MessageBox, Message } from 'element-ui'; // 假设使用Element UI的提示组件
import router from '@/router'; // 你的路由实例
// 创建实例
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // 从环境变量读取,非常灵活!
timeout: 10000, // 10秒超时
});
// 请求拦截器
service.interceptors.request.use(
(config) => {
// 在发送请求之前做些什么
const token = localStorage.getItem('access-token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
// 可以根据需要添加其他逻辑,比如显示全局Loading...
// showFullScreenLoading();
return config;
},
(error) => {
// 对请求错误做些什么
// hideFullScreenLoading();
console.error('Request Error:', error);
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
(response) => {
// hideFullScreenLoading();
const res = response.data;
// 假设我们的后端统一返回格式为 { code: 200, data: ..., message: 'success' }
if (res.code === 200) {
return res.data; // 返回我们真正需要的数据
} else {
// 业务逻辑错误(比如code=50001代表密码错误)
// 统一提示错误信息
Message({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000,
});
// 可以针对特定的code做特殊操作,比如token过期
if (res.code === 50008 || res.code === 50012) {
// 触发登出逻辑,清除token,跳转登录页
MessageBox.confirm('登录状态已过期,请重新登录', '确认登出', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
localStorage.removeItem('access-token');
router.push('/login');
});
}
return Promise.reject(new Error(res.message || 'Error'));
}
},
(error) => {
// hideFullScreenLoading();
console.error('Response Error:', error);
let { message } = error;
if (error.response) {
// 有响应但状态码不是2xx
switch (error.response.status) {
case 400:
message = '请求错误';
break;
case 401:
message = '未授权,请登录';
// 跳转登录页
setTimeout(() => {
router.push('/login');
}, 1000);
break;
case 403:
message = '拒绝访问';
break;
case 404:
message = `请求地址出错: ${error.response.config.url}`;
break;
case 500:
message = '服务器内部错误';
break;
case 502:
message = '网关错误';
break;
default:
message = '网络连接错误';
}
} else if (error.request) {
// 请求发出去了但没有收到响应
message = '网络连接异常,请检查您的网络!';
} else {
// 在设置请求时触发了一些错误
message = error.message;
}
Message({
message,
type: 'error',
duration: 5 * 1000,
});
return Promise.reject(error);
}
);
export default service;
在组件中使用 (SomeComponent.vue)
<template>
<div>
<ul>
<li v-for="user in userList" :key="user.id">{{ user.name }}</li>
</ul>
</div>
</template>
<script>
import apiClient from '@/utils/request'; // 导入我们封装的实例
export default {
name: 'UserList',
data() {
return {
userList: []
};
},
async created() {
await this.fetchUsers();
},
methods: {
async fetchUsers() {
try {
// 简洁,清晰,专注业务逻辑!
this.userList = await apiClient.get('/users');
} catch (error) {
// 错误已经被拦截器统一处理,这里通常无需再写通用错误提示
// 但如果这个页面有特殊的错误处理逻辑,可以在这里写
console.error('获取用户列表失败,需要特殊处理:', error);
}
}
}
};
</script>
结语:从“会用”到“用好”
看到这里,你是不是已经彻底明白,为啥创建Axios实例是Vue项目开发的“必修课”了?它不仅仅是一个API,更是一种工程化思想和代码组织能力的体现。
从此以后,请告别那种“打零工”式的原始axios用法。拥抱“创造”,打造属于你自己的、功能强大且优雅的HTTP客户端。你的代码会感谢你,你的队友也会感谢你!
好了,今天的“不发疯指南”就到这里。快去给你的项目也“雇佣”一个得力的Axios“正式工”吧!

被折叠的 条评论
为什么被折叠?



