封装网络请求
前言
最近开新项目又得封装网络请求, 之前的用响应式变量有点折磨, 这次直接用 axios 了, 正好水一篇博客吧.
基本用法
javascript
import axios from 'axios'
const instance = axios.create({
baseURL: 'http://localhost:3000',
timeout: 5000,
})
export { instance }
对 response 和 request 的处理
之后 axios.get 或者 post 什么的就行了. 但是用的时候会发现, 每次都需要这样子
javascript
// 这里假设100为请求成功, 1为token无效或缺少
//
// interface Response{
// message: string,
// code: number,
// data: any
// }
//
//
instance.get('/info').then((res) => {
if (!res.data) {
// 数据为空
return
}
const { code } = res.data
if (code === 1) {
router.push('/login')
userStore.clear()
showMsg('请重新登录')
}
const { data } = resp.data
return data
})
这样的话每次都需要判断 code, 然后从 res.data 里拿 data. 在页面里面其实并不关心 code, 如果能找个什么地方统一处理的话可以省好多事情.
axios 提供了两个拦截器, axios.interceptors.request.use
和axios.interceptors.response.use
, 一个针对请求, 一个针对回复. 在这里可以用回复来统一处理
javascript
const codeHandler = {
// 处理业务错误
1: () => {
router.push('/login')
userStore.clear()
showMsg('请重新登录')
},
}
const httpCodeHandler = {
// 处理http状态码错误
// ...
}
instance.interceptors.response.use(
(resp) => {
const { code } = resp.data
if (code !== 100) {
codeHandler[code]?.(resp)
return promise.reject(resp)
}
const { data } = resp.data
return data
},
(error) => {
const { status } = error
httpCodeHandler[status]?.()
return Promise.reject(error)
}
)
这样子的话每次请求就可以直接使用 data 而不需要在意 code 了
javascript
instance
.get('/info')
.then((data) => {
// 做点成功做的事情
})
.catch((err) => {
console.error(err)
})
这里也可以用一下 request 的拦截器, 之后不用再手动加 token
javascript
instance.interceptors.request.use((config) => {
if (!config.url.startsWith('/auth')) {
// 不需要鉴权
return config
}
// 获取token操作
const token = localstorage.getItem('token')
if (!token) {
router.push('/login')
return
}
config.headers.Authorization = `Bearer ${token}`
return config
})
解耦合
写到这里之后用的还算方便, 如果业务逻辑一直这么简单的话. 但是随着项目的庞大, 这个可能就存在一点问题. 现在的话其实路由/页面、仓库、网络请求耦合在一起了, 在更加复杂的情况下要做修改就会相当痛苦. 这里可以用发布订阅的设计模式来解耦合.
javascript
class EventBus {
on(eventName, listener) {
if (!this.listeners[eventName]) {
this.listeners[eventName] = new Set()
}
this.listeners[eventName].add(listener)
}
emit(eventName, ...args) {
this.listeners[eventName].forEach(listener => listener(...args))
}
off(eventName, listener) {
if (!this.listeners[eventName]) {
throw new Error(`Event with name ${eventName} is not found`)
}
this.listeners[eventName].delete(listener)
}
}
export default new EventBus()
这样的话, 网络请求这里就不必再导入 useUserStore 和 useRouter 之类的函数了, 只需要导入 EventBus, 然后这样
网络请求
javascript
const codeHandler = {
// 处理业务错误
1: () => {
eventBus.emit('API:UN_AUTH')
},
}
仓库
javascript
export const useUserStore = defineStore('user', () => {
eventBus.on('API:UN_AUTH', () => {
logout()
})
const userInfo = ref()
const setUser = (user) => {
userInfo.value = user
}
const logout = () => {
userInfo.value = null
localstorage.removeItem('token')
}
return {
userInfo,
setUser,
logout,
}
})
路由
javascript
const router
= createRouter()
// ...
eventEmitter.on('API:UN_AUTH', () => {
router.push('/auth/login')
})
页面
javascript
eventEmitter.on('API:UN_AUTH', () => {
showMsg('...')
})
这样这几个部分只需要在自己的地方做好自己的事情就可以了.