在Vue项目里做数据请求,Axios几乎是绕不开的工具,但新手刚接触时,总会碰到“怎么引入?请求咋写?跨域咋解决?”这些问题,这篇文章把Vue和Axios结合时的常见问题拆成一个个小问题,从基础用法到实战优化,一步步讲清楚,帮你把Axios用得顺手~
Axios是个基于Promise的HTTP客户端,既能在浏览器里发请求(处理浏览器的XMLHttpRequest),也能在Node.js环境用(基于http模块),它和Vue搭配流行,核心是这几点: - **和Vue的异步逻辑契合**:Vue里常用的`async/await`、生命周期钩子(created`里发请求),和Axios的Promise风格天然适配,写异步代码更丝滑; - **拦截器是“神助攻”**:能在请求发出去前、响应回来后“插一脚”——比如请求前统一加token、响应后统一处理错误,不用每个请求重复写逻辑; - **上手门槛低**:API设计简单,`get` `post`这些方法一看就懂,还能灵活配置超时、请求头,对前端同学友好。
Vue项目里咋引入Axios?
分“全局挂载”和“实例化”两种思路,按需选:
方式1:全局挂载(简单直接)
先装依赖:用npm install axios或者yarn add axios把Axios装到项目里。
然后在Vue的入口文件(比如main.js)里全局注册:
import Vue from 'vue' import axios from 'axios' // 把axios挂到Vue原型上,所有组件都能通过this.$axios调用 Vue.prototype.$axios = axios
之后在组件里就能直接用了,比如在created钩子发请求:
export default {
created() {
this.$axios.get('/api/users').then(res => {
console.log(res.data)
})
}
}方式2:实例化(灵活配置多环境)
如果项目有多个不同域名的接口(比如开发、测试、生产环境),或者需要不同的超时、请求头,可以创建自定义Axios实例:
import axios from 'axios'
// 创建实例,配置基地址、超时等
const service = axios.create({
baseURL: 'https://api.example.com', // 接口基地址
timeout: 5000, // 请求超时时间(毫秒)
headers: { 'X-Custom-Header': 'xxx' } // 自定义请求头
})
// 把实例导出,组件里按需导入
export default service这样不同模块能复用不同实例,比如用户模块用userService,商品模块用goodsService,彼此配置互不影响。
最基础的GET/POST请求咋写?
用async/await(推荐,代码更简洁)或者.then()都能写,举两个常用场景:
GET请求(带查询参数)
比如获取用户列表,需要传page和size参数:
async getUsers() {
try {
// params里的参数会自动拼到url后面(?page=1&size=10)
const res = await this.$axios.get('/users', {
params: { page: 1, size: 10 }
})
this.userList = res.data // 把响应数据存到组件data里
} catch (err) {
console.error('请求失败:', err)
}
}POST请求(传请求体)
比如新增用户,要把用户信息传到后端:
async addUser() {
const userInfo = { name: '小明', age: 20 }
try {
// data里的内容会放到请求体(Request Body)里
const res = await this.$axios.post('/users', userInfo)
if (res.data.code === 200) { // 假设后端返回code=200代表成功
this.$toast('添加成功~') // 用UI库提示成功
}
} catch (err) {
this.$toast('添加失败,请重试~')
}
}拦截器有啥用?咋配置?
拦截器就像“中间件”,能在请求发出去前和响应回来后做统一处理,避免重复代码。
请求拦截器:加token、开loading
比如每个请求都要带用户token,或者发请求时显示“加载中”:
// 给全局axios加请求拦截器
axios.interceptors.request.use(
config => {
// 1. 加token:从localStorage取token,塞到请求头
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
// 2. 开loading:比如用Element UI的Loading组件
// Loading.service({ fullscreen: true })
return config // 必须返回config,请求才会继续
},
error => {
// 请求配置出错时(比如url写错),直接拒绝
return Promise.reject(error)
}
)响应拦截器:解包数据、统一错误处理
后端返回的数据可能包了一层(比如{ code, data, msg }),或者要处理401、500这些错误:
axios.interceptors.response.use(
response => {
// 1. 关loading
// Loading.service().close()
// 2. 解包数据:只返回业务需要的data
return response.data
},
error => {
// 处理错误:比如401跳登录,500提示服务器错误
if (error.response.status === 401) {
router.push('/login') // 跳转到登录页
} else {
// 用UI库提示错误信息
Message.error(error.response.data.msg || '服务器开小差啦~')
}
return Promise.reject(error) // 把错误抛给组件
}
)注意:如果用了自定义实例(比如前面的service),拦截器要挂在实例上:service.interceptors.request.use(...)
跨域问题咋解决?开发和生产不一样咋办?
浏览器有“同源策略”(协议、域名、端口必须一致才算同源),前端发请求到不同源的后端就会跨域,分开发阶段和生产阶段处理:
开发阶段:用Vue CLI代理
在vue.config.js里配置devServer.proxy,把前端请求“代理”到后端域名:
module.exports = {
devServer: {
proxy: {
'/api': { // 匹配以/api开头的请求
target: 'https://api.example.com', // 后端真实地址
changeOrigin: true, // 开启跨域(让后端以为请求来自自己的域名)
pathRewrite: { '^/api': '' } // 把/api替换成空,比如请求/api/users → 实际发往https://api.example.com/users
}
}
}
}这样前端请求时,用/api开头就不会跨域,比如this.$axios.get('/api/users')。
生产阶段:后端开CORS或Nginx代理
后端开CORS:后端在响应头加
Access-Control-Allow-Origin: *(或指定前端域名),前端不用改代码,浏览器就允许跨域请求;Nginx反向代理:把前端和后端部署在同一域名下,用Nginx转发请求,比如前端部署在
example.com,后端接口是api.example.com,Nginx配置把/example-api请求转发到api.example.com,前端请求/example-api/users就不会跨域。
重复请求和竞态问题咋处理?
用户快速点按钮、多次发同一请求,或者请求A和请求B顺序乱了,都会导致问题,这两种场景要分开处理:
场景1:重复请求(比如快速点按钮)
用AbortController(现代浏览器支持)取消之前的请求:
let abortController = null // 存当前请求的控制器
async function fetchData() {
if (abortController) {
abortController.abort() // 取消上一次请求
}
abortController = new AbortController() // 新建控制器
try {
const res = await axios.get('/data', {
signal: abortController.signal // 把信号传给请求
})
// 处理响应数据
} catch (err) {
if (err.name === 'AbortError') {
console.log('上一次请求被取消啦~')
} else {
console.error('真的出错了:', err)
}
}
}场景2:竞态问题(请求顺序乱了)
比如先搜“苹果”再搜“香蕉”,但“苹果”的请求后返回,导致页面显示错误,解决思路:
用时间戳标记请求:每次发请求时记录时间戳,只处理最后一次请求的响应;
组件销毁时取消请求:在Vue的
beforeDestroy钩子里,取消还没完成的请求;防抖/节流:比如搜索框输入时,用户停止输入1秒后再发请求,避免每次输入都发请求。
错误处理咋做更友好?
用户最讨厌“请求失败”却不知道为啥失败,要区分网络错误、业务错误(比如参数错)、服务器错误,给明确反馈:
全局统一处理(响应拦截器)
在响应拦截器里写通用错误逻辑,比如前面讲的响应拦截器示例,能覆盖大部分错误,再封装个函数,把错误类型拆细:
function handleError(error) {
if (error.response) {
// 服务器返回错误(状态码不是2xx)
const { status, data } = error.response
if (status === 400) {
Message.error(data.msg || '参数填错啦~')
} else if (status === 500) {
Message.error('服务器忙,稍等再试~')
}
} else if (error.request) {
// 请求发出去了,但没收到响应(比如断网)
Message.error('网络开小差了,检查下WiFi?')
} else {
// 其他错误(比如配置错)
Message.error('请求配置出错:' + error.message)
}
}组件内兜底处理
在组件里用try...catch或者.catch(),兜底捕获没被全局拦截的错误:
async getInfo() {
try {
const res = await this.$axios.get('/info')
this.info = res.data
} catch (err) {
handleError(err) // 调用全局错误处理函数
}
}怎么封装Axios更高效?
项目大了,每个请求都写axios.get axios.post会很冗余,封装后,统一配置、统一错误处理,维护起来更爽:
步骤1:创建实例+配置拦截器
在utils/request.js里写:
import axios from 'axios'
import { Message } from 'element-ui' // 假设用Element UI的提示
// 创建自定义实例
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // 从环境变量取基地址(不同环境不同值)
timeout: 10000, // 超时时间
headers: { 'Content-Type': 'application/json;charset=utf-8' }
})
// 请求拦截器:加token
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 => {
const res = response.data
if (res.code !== 200) { // 假设业务成功码是200
Message.error(res.msg || '操作失败')
return Promise.reject(new Error(res.msg || 'Error'))
}
return res // 只返回业务数据
},
error => {
handleError(error) // 调用前面的错误处理函数
return Promise.reject(error)
}
)步骤2:封装通用请求方法
把get post这些方法封装好,组件里直接导入用:
// 封装get请求
export function get(url, params) {
return service.get(url, { params })
}
// 封装post请求
export function post(url, data) {
return service.post(url, data)
}
// 其他请求方法(put、delete等)同理步骤3:组件里用封装后的方法
比如用户模块的组件:
import { get, post } from '@/utils/request' // 导入封装的方法
export default {
data() {
return { userList: [] }
},
async created() {
// 获取用户列表
const res = await get('/users', { page: 1, size: 10 })
this.userList = res.data
},
methods: {
async addUser() {
const user = { name: '小红', age: 22 }
await post('/users', user)
this.$toast('添加成功~')
}
}
}这样封装后,后期要改基地址、超时时间,只需要改utils/request.js,不用每个组件到处找请求代码,维护性拉满~
吃透上面这些问题,Vue项目里的Axios基本就“玩得转”了,从基础请求到拦截器、跨域、错误处理再到封装,每一步都是开发里高频遇到的场景,把这些逻辑理顺,不仅写代码效率高,用户体验也会更流畅~






网友评论文明上网理性发言 已有0人参与
发表评论: