Vue 拦截器教程
概念与适用场景
在 Vue 项目中,“拦截器”通常指基于 Axios 的 HTTP 拦截器,用于在请求发送前与响应返回后统一处理逻辑,例如:添加通用请求头(如 Authorization)、统一错误处理、格式化响应数据、记录日志等。与之配合的还有 Vue Router 的路由守卫,用于在页面跳转时进行权限校验与重定向。两者结合,可实现鉴权、登录跳转、全局加载与错误提示等常见能力。
HTTP 拦截器快速上手
- 安装与创建实例
使用 axios.create 创建独立实例,避免污染全局配置,并便于按环境切换 baseURL、timeout 等。 - 添加请求拦截器
在请求发出前统一处理,如注入 token、设置超时、开启全屏 Loading 等。 - 添加响应拦截器
统一处理业务状态码与 HTTP 状态码,如遇到 401 清除本地凭证并跳转登录页。 - 在组件中使用
直接使用封装好的实例发起请求,拦截器会自动生效。
示例代码(src/utils/request.js):
// src/utils/request.js
import axios from 'axios'
import router from '@/router'
import { Message } from 'element-ui' // 可替换为你项目的提示组件const http = axios.create({baseURL: import.meta.env.VITE_API_BASE_URL || '/api',timeout: 10000
})// 请求拦截器
http.interceptors.request.use((config) => {const token = localStorage.getItem('token')if (token) {config.headers.Authorization = `Bearer ${token}`}// 可在此开启全屏 loading// store.dispatch('app/showLoading', true)return config},(error) => Promise.reject(error)
)// 响应拦截器
http.interceptors.response.use((response) => {// 可在此关闭全屏 loading// store.dispatch('app/showLoading', false)// 若后端约定 data 为业务数据,可直接返回 response.datareturn response},(error) => {// 关闭全屏 loading// store.dispatch('app/showLoading', false)if (error.response) {const { status, data } = error.responseif (status === 401) {localStorage.removeItem('token')router.replace({path: '/login',query: { redirect: router.currentRoute.fullPath }})} else {// 业务或 HTTP 错误提示Message.error(data?.message || `请求失败(${status})`)}} else if (error.request) {Message.error('网络错误,请检查网络连接')} else {Message.error('请求配置错误:' + error.message)}return Promise.reject(error)}
)export default http在入口文件挂载(可选,便于在插件/工具中直接使用):
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import http from './utils/request'const app = createApp(App)
app.config.globalProperties.$http = http
app.use(router).mount('#app')在组件中使用:
// 选项式 API
import http from '@/utils/request'
export default {async created() {try {const res = await http.get('/users')console.log(res.data)} catch (err) {// 错误已在拦截器中统一提示,此处可按需处理}}
}// 组合式 API
import { getCurrentInstance } from 'vue'
import http from '@/utils/request'
export default {setup() {const { proxy } = getCurrentInstance()const fetchData = async () => {const res = await http.get('/users')console.log(res.data)}return { fetchData }}
}路由拦截与鉴权
- 在路由定义中使用 meta 字段 标识需要登录的页面(如 requireAuth: true)。
- 使用 router.beforeEach 全局前置守卫进行拦截:若目标路由需要登录且本地无 token,则重定向到登录页,并携带 redirect 以便登录后回跳。
- 结合 HTTP 拦截器处理 401:当后端返回未授权时,清除本地凭证并跳转登录,保证前端与后端状态一致。
示例代码(src/router/index.js):
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Login from '@/views/Login.vue'
import Home from '@/views/Home.vue'const router = createRouter({history: createWebHistory(),routes: [{ path: '/login', name: 'Login', component: Login, meta: { auth: false } },{ path: '/', name: 'Home', component: Home, meta: { requireAuth: true } }]
})router.beforeEach((to, from, next) => {const token = localStorage.getItem('token')const requiresAuth = to.meta.requireAuthif (requiresAuth && !token) {next({path: '/login',query: { redirect: to.fullPath }})} else {next()}
})export default router最佳实践与常见问题
- 使用独立实例
通过 axios.create 创建实例并导出,避免直接修改全局 axios,便于多环境与多业务线维护。 - 不要在拦截器中放入副作用逻辑
如非必要,避免在拦截器内直接修改全局状态或触发复杂副作用,保持拦截器职责单一与可预测性。 - 区分 HTTP 状态码与业务状态码
401/403/500 等为 HTTP 状态码,通常在响应拦截器处理;业务成功与否常由后端在响应体中的 code/message 字段表达,需按业务约定判断并在拦截器或业务层处理。 - 统一错误提示与加载状态
可在响应拦截器中集成全局提示(如 Message)与 Loading,避免每个请求重复编写,注意避免重复关闭或在错误分支遗漏关闭。 - 登录跳转携带目标路径
使用query: { redirect: to.fullPath }保存来源地址,登录成功后跳转回原页面,提升体验。 - 配置与环境分离
将 baseURL、timeout、withCredentials 等抽离到环境变量或配置文件,便于开发、测试、生产多环境切换。
