在 Vue 項目中,使用 axios
進行 HTTP 請求是非常常見的做法。為了提高代碼的可維護性、統一錯誤處理和請求攔截/響應攔截邏輯,對axios
進行封裝使用。
一、基礎封裝(適用于 Vue 2 / Vue 3)
1. 安裝 axios
npm install axios
2. 創建封裝文件:src/utils/request.js
import axios from 'axios'
import { Message } from 'element-ui' // 或你使用的 UI 庫// 創建 axios 實例
const service = axios.create({baseURL: process.env.VUE_APP_API, // 設置默認 base URL(來自 .env)timeout: 5000, // 超時時間withCredentials: false // 是否攜帶 cookie
})// 請求攔截器
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.dataif (res.code !== 200) {Message.error(res.message || 'Error')if (res.code === 401) {// 處理 token 失效localStorage.removeItem('token')window.location.href = '/login'}return Promise.reject(new Error(res.message || 'Error'))} else {return res}},error => {Message.error('網絡異常,請檢查網絡連接')return Promise.reject(error)}
)export default service
二、使用封裝后的 axios
1. 在組件中直接調用
import request from '@/utils/request'export default {methods: {async fetchData() {try {const res = await request.get('/api/data')console.log(res)} catch (error) {console.error(error)}}}
}
2. 在 Vuex 中使用
import request from '@/utils/request'export default {actions: {async login({ commit }, payload) {const res = await request.post('/api/login', payload)commit('SET_TOKEN', res.token)}}
}
三、支持 TypeScript(可選)
如果你使用的是 Vue + TypeScript(如 Vite + Vue 3 + TS),可以添加類型定義:
1. 定義統一返回結構
// src/types/index.ts
export interface ApiResponse<T = any> {code: numbermessage: stringdata: T
}
2. 使用泛型調用
import { ApiResponse } from '@/types'interface User {id: numbername: string
}const res = await request.get<ApiResponse<User>>('/api/user/1')
console.log(res.data.name)
四、取消重復請求(防抖)
建議常用事件無論是否發生請求也做防抖,避免重復性耗費資源
防止用戶多次點擊按鈕導致重復請求:
import axios from 'axios'const pendingMap = new Map()function generateReqKey(config) {return [config.method, config.url].join('&')
}function addPending(config) {const key = generateReqKey(config)const controller = new AbortController()config.signal = controller.signalif (!pendingMap.has(key)) {pendingMap.set(key, controller)}
}function removePending(key) {if (pendingMap.has(key)) {const controller = pendingMap.get(key)controller.abort()pendingMap.delete(key)}
}// 請求攔截器
service.interceptors.request.use(config => {addPending(config)return config
})// 響應攔截器
service.interceptors.response.use(response => {removePending(generateReqKey(response.config))return response
}, error => {removePending(generateReqKey(error.config))return Promise.reject(error)
})
五、全局 loading(可選)
可以在請求攔截器中添加 loading,在響應攔截器中關閉:
let loadingCount = 0function startLoading() {if (loadingCount === 0) {// 顯示 loading 動畫store.dispatch('showLoading')}loadingCount++
}function endLoading() {loadingCount--if (loadingCount <= 0) {store.dispatch('hideLoading')}
}// 請求攔截器
service.interceptors.request.use(config => {startLoading()return config
})// 響應攔截器
service.interceptors.response.use(response => {endLoading()return response
}, error => {endLoading()return Promise.reject(error)
})
六、推薦目錄結構
src/
├── utils/
│ └── request.js # axios 封裝
├── types/
│ └── index.ts # 接口類型定義(TS)
├── api/
│ ├── user.js # 用戶相關接口
│ ├── product.js # 商品相關接口
│ └── index.js # 導出所有 API
├── views/
│ └── ... # 頁面組件
└── store/└── index.js # Vuex 狀態管理(可選)
七、API 模塊化示例(src/api/user.js
)
import request from '@/utils/request'export function login(data) {return request({url: '/user/login',method: 'post',data})
}export function getUserInfo(token) {return request({url: '/user/info',method: 'get',params: { token }})
}
在組件中使用:
import { login } from '@/api/user'export default {methods: {async handleLogin() {const res = await login(this.loginForm)console.log(res)}}
}
總結:要點
特性 | 實現方式 |
---|---|
請求攔截 | 添加 token、loading |
響應攔截 | 統一處理成功/失敗邏輯 |
錯誤提示 | 使用 UI 框架提示(如 element-ui、vant) |
類型安全 | TypeScript 泛型支持 |
取消重復請求 | 使用 AbortController |
模塊化組織 | 按功能拆分 API 文件 |
全局 loading | 請求計數器控制顯示/隱藏 |