一、Axios 是啥?为啥 Vue2 项目里选它做请求?
不少同学刚接触 Vue2 做项目时,一碰到网络请求就犯懵 —— Axios 到底咋用?不同请求咋发?拦截器、封装这些概念咋落地?跨域报错咋解决?这篇把 Vue2 + Axios 开发里的常见问题拆碎了讲,从基础到实战一步步说清楚,新手也能跟着理顺思路~
Axios 是个基于 Promise 的 HTTP 客户端,浏览器和 Node.js 环境都能跑,Vue2 生态里常用它做请求,核心原因有这些: - Vue 本身不内置请求库,得自己选工具; - Axios 语法友好,用 `axios.get()` `axios.post()` 这类链式调用,比原生 `fetch` 或 `XMLHttpRequest` 好懂; - 支持**拦截器**(请求发出去前、响应回来后统一处理)、**取消请求**(避免重复请求)、**自动转换 JSON** 这些实用特性; - 社区资源多,碰到问题一搜一大把解决方案,学习成本低。Vue2 项目里咋引入 Axios?全局和局部用法有啥区别?
先把 Axios 装到项目里:用 npm install axios 或者 yarn add axios 都行,接下来分全局引入和局部引入两种方式:
全局引入(推荐高频请求场景)
在项目的 main.js 里这么写:
import Vue from 'vue' import axios from 'axios' // 把 axios 挂到 Vue 原型上,所有组件都能通过 this.$axios 调用 Vue.prototype.$axios = axios
这样做的好处是不用每个组件都重复 import,写请求时直接 this.$axios.get(...) 就行,但如果项目里有多个不同域名的接口(比如一个调内部系统,一个调第三方),全局配置可能不够灵活,这时候得结合「实例化封装」(后面会讲)。
局部引入(适合低频/特殊配置请求)
如果只是某个组件要用 Axios,就在组件里单独引入:
import axios from 'axios'
export default {
methods: {
fetchData() {
axios.get('/api/data').then(...)
}
}
}
局部引入的优势是配置更灵活,比如给这个请求单独设超时时间、请求头,但缺点是代码重复率高,多个组件用的话得重复写 import。
最基础的 GET/POST 请求咋写?参数咋传?
实际开发里,GET 常用在「查数据」(比如列表查询),POST 常用在「提交数据」(比如登录、上传),这两种请求的参数传递方式不一样,得注意细节:
GET 请求:参数走 params(自动拼到 URL 后)
比如做一个「文章列表查询」,需要传页码和每页数量:
this.$axios.get('/api/articles', {
params: {
page: 1,
size: 10
}
}).then(res => {
console.log(res.data) // 后端返回的数据
}).catch(err => {
console.error('请求失败:', err)
})
Axios 会自动把 params 里的对象转成 ?page=1&size=10 拼到 URL 后面,还会帮你处理特殊字符的编码,不用自己操心。
POST 请求:参数走 data(默认发 JSON)
用户登录」需要传账号密码,用 POST 发 JSON 数据:
this.$axios.post('/api/login', {
username: 'test',
password: '123456'
}).then(res => {
// 登录成功逻辑,比如存 token
}).catch(err => {
// 错误处理,比如提示“账号密码错误”
})
如果后端需要「表单格式(form-data)」(比如上传文件),得用 FormData 构造数据,还要手动设请求头:
// 假设 fileObj 是文件对象(<input type="file"> 选的文件)
let formData = new FormData()
formData.append('file', fileObj)
this.$axios.post('/api/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
这里要注意:Axios 默认 POST 请求头是 application/json,如果要发 form-data,必须手动把请求头改成 multipart/form-data,否则后端可能解析不了。
拦截器有啥用?请求和响应拦截器咋配置?
拦截器能在「请求发出去前」和「响应回来后」做统一处理,减少重复代码,比如给所有请求加 token、全局处理错误码,都靠它:
请求拦截器:给所有请求加 token
很多接口需要验证身份,得在请求头里带 token,用请求拦截器,不用每个请求单独写:
// 先引入 axios(如果是全局配置,直接用 Vue.prototype.$axios)
import axios from 'axios'
axios.interceptors.request.use(
config => {
// 从 localStorage 里拿 token(假设登录后存在这里)
const token = localStorage.getItem('token')
if (token) {
// 把 token 加到请求头,格式看后端要求,这里是 Bearer 格式举例
config.headers.Authorization = `Bearer ${token}`
}
return config // 必须 return 配置,请求才会发出去
},
error => {
// 请求还没发就出错了(比如参数错了),直接 reject
return Promise.reject(error)
}
)
这样配置后,所有通过这个 axios 实例发的请求,请求头都会自动带上 token,不用每个请求手动写。
响应拦截器:全局处理错误码
后端接口一般会返回统一的结构({ code, data, msg }),用响应拦截器可以提前处理错误:
axios.interceptors.response.use(
response => {
// 假设后端约定 code=200 是成功,其他是业务错误
if (response.data.code === 401) {
// 401 代表未登录,跳转到登录页(用 Vue Router 的话,this.$router.push)
window.location.href = '/login'
} else if (response.data.code !== 200) {
// 业务错误,密码错误”“权限不足”,全局提示
alert(response.data.msg)
}
// 把响应里的 data 直接返回,组件里不用再写 res.data
return response.data
},
error => {
// 处理网络错误(比如超时、断网)
if (error.message.includes('timeout')) {
alert('请求超时,请重试')
} else if (!window.navigator.onLine) {
alert('网络连接异常')
}
return Promise.reject(error)
}
)
配置后,组件里调接口时,then 里直接拿到的是 response.data,不用再判断 code,代码更简洁。
为啥要封装 Axios?怎么封装更实用?
直接用全局 axios 有个问题:不同环境(开发、测试、生产)的基础 URL 不好切换,而且拦截器改动得改全局配置,维护起来麻烦。「封装 Axios」就是创建自定义实例,让配置更灵活:
步骤:创建实例 + 加拦截器 + 导出
在项目里新建 utils/request.js(路径自己定),写这样的代码:
import axios from 'axios'
// 创建 axios 实例,自定义配置
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // 不同环境配 .env 文件(比如开发环境是 /api,生产是 https://xxx.com)
timeout: 5000, // 全局超时时间,5 秒没响应就算超时
headers: { 'Content-Type': 'application/json' } // 全局请求头
})
// 给实例加请求拦截器(和之前全局配置逻辑一样,只是作用于这个实例)
service.interceptors.request.use(
config => {
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 === 401) {
window.location.href = '/login'
} else if (response.data.code !== 200) {
alert(response.data.msg)
}
return response.data
},
error => {
if (error.message.includes('timeout')) {
alert('请求超时')
}
return Promise.reject(error)
}
)
// 导出实例,其他文件用这个实例发请求
export default service
组件里咋用封装后的实例?
在需要发请求的组件里,引入这个实例:
import request from '@/utils/request' // 路径对应你放 request.js 的位置
export default {
methods: {
fetchArticle() {
request.get('/articles', {
params: { page: 1 }
}).then(res => {
// res 已经是 response.data 了,直接用
}).catch(err => {
// 错误处理
})
}
}
}
封装的好处很明显:不同模块可以创建多个实例(比如一个调内部接口,一个调第三方接口),每个实例的基础 URL、超时时间、拦截器都是独立的,维护起来更灵活。
Vue2 里 Axios 跨域问题咋解决?开发和生产有啥区别?
跨域是浏览器的「同源策略」导致的(协议、域名、端口有一个不一样,就会跨域),开发和生产环境的解决思路不同:
开发环境(Vue CLI 项目):用代理转发
在项目根目录的 vue.config.js 里配置代理:
module.exports = {
devServer: {
proxy: {
'/api': { // 匹配所有以 /api 开头的请求
target: 'https://xxx.com', // 后端真实域名
changeOrigin: true, // 开启代理:虚拟一个和后端同域的请求源,绕开浏览器拦截
pathRewrite: { '^/api': '' } // 把 /api 替换成空,比如请求 /api/login 实际发到 https://xxx.com/login
}
}
}
}
配置后,前端发请求到 /api/login,会被代理到 https://xxx.com/login,浏览器里看请求是前端自己的域名(http://localhost:8080),就不会触发跨域报错。
生产环境:用 Nginx 反向代理
开发时的 devServer.proxy 只在本地生效,生产环境得用 Nginx 配置反向代理,在 Nginx 的配置文件(nginx.conf)里加:
server {
listen 80; # 前端项目运行的端口
server_name 你的域名; # www.xxx.com
location /api { # 匹配 /api 开头的请求
proxy_pass https://xxx.com; # 后端真实地址
proxy_set_header Host $host; # 把请求头里的 Host 设为前端域名,让后端识别
proxy_set_header X-Real-IP $remote_addr; # 传递客户端真实 IP
}
}
这样生产环境下,前端请求 /api/login 会被 Nginx 转发到后端,同样绕开跨域问题。
Axios 错误处理咋做更完善?
除了拦截器里的全局处理,组件里调接口时也要「分层处理」,区分网络错误和业务错误:
组件内的错误处理逻辑
假设用封装后的 request 实例发请求,代码可以这么写:
request.post('/login', { username, password })
.then(res => {
// 拦截器已经把 response.data 返回来,这里 res 就是后端数据
if (res.code === 200) {
// 登录成功:存 token、跳首页等
localStorage.setItem('token', res.data.token)
this.$router.push('/home')
} else {
// 业务错误:账号不存在”“密码错误”,给用户提示
alert(res.msg)
}
})
.catch(err => {
// 网络错误:比如拦截器里没处理完的超时、断网
console.log('请求失败原因:', err)
alert('网络波动,请稍后再试')
})
进阶:取消重复请求
如果用户快速点按钮,同一个请求发了多次,会给后端增加压力,用 Axios 的 CancelToken 可以取消重复请求:
// 在 request.js 里加这段逻辑(给封装的实例用)
const pending = new Map() // 存请求标识和取消函数
// 生成请求唯一标识(method + url + params + data)
const getRequestKey = (config) => {
return [config.method, config.url, JSON.stringify(config.params), JSON.stringify(config.data)].join('&')
}
// 请求拦截器里加“取消重复请求”逻辑
service.interceptors.request.use(config => {
const key = getRequestKey(config)
// 如果之前有相同的请求在pending,取消它
if (pending.has(key)) {
pending.get(key)() // 执行取消操作
pending.delete(key) // 从map里删掉
}
// 给当前请求加 CancelToken
config.cancelToken = new axios.CancelToken(cancel => {
pending.set(key, cancel)
})
return config
}, error => {
return Promise.reject(error)
})
// 响应拦截器里移除已完成的请求
service.interceptors.response.use(response => {
const key = getRequestKey(response.config)
pending.delete(key) // 请求完成,从map里删掉
return response
}, error => {
// 如果是主动取消的请求,不视为错误
if (axios.isCancel(error)) {
return new Promise(() => {}) // 空Promise,组件里catch不会触发
}
return Promise.reject(error)
})
这样配置后,同一时间重复的请求会被自动取消,减少后端压力。
Axios 和 Vuex 结合咋玩?异步请求咋管理状态?
Vuex 里的 action 适合处理异步操作(比如登录、获取用户信息),可以把请求逻辑放到 action 里,用 mutation 更新状态:
例子:用户登录 + 获取信息流程
在 store/modules/user.js 里写:
import request from '@/utils/request' // 引入封装好的 axios 实例
const state = {
token: '',
userInfo: {}
}
const mutations = {
// 存 token
SET_TOKEN(state, token) {
state.token = token
},
// 存用户信息
SET_USER_INFO(state, info) {
state.userInfo = info
}
}
const actions = {
// 登录 action,接收用户名和密码
login({ commit }, { username, password }) {
return new Promise((resolve, reject) => {
request.post('/login', { username, password })
.then(res => {
if (res.code === 200) {
// 登录成功,存 token
commit('SET_TOKEN', res.data.token)
// 接着获取用户信息
return request.get('/user/info')
}
return res // code 不对,把结果抛出去
})
.then(userRes => {
// 存用户信息
commit('SET_USER_INFO', userRes.data)
resolve(userRes) // 通知组件登录成功
})
.catch(err => {
reject(err) // 通知组件登录失败
})
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
组件里触发这个 action:
export default {
methods: {
handleLogin() {
this.$store.dispatch('user/login', {
username: this.username,
password: this.password
})
.then(() => {
// 登录成功,跳转到首页
this.$router.push('/home')
})
.catch(err => {
// 登录失败,提示错误
alert('登录失败:' + err.message)
})
}
}
}
把请求逻辑放到 Vuex 的 action 里,状态更新用 mutation,组件只负责「触发 action」和「处理结果」,代码分层更清晰,维护起来也方便。
实际开发里还有哪些 Axios 小技巧?
除了基础用法,这些细节能帮你避坑:
参数序列化:复杂对象转 URL 参数
GET
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网


