前端开发通常会对 axios 网络请求库进行二次封装,对于不同的响应码进行不同的处理。比如 401 的时候跳转到登录页面,403 的时候跳转到权限页面,500 的时候跳转到错误页面。
// request.ts
const request = axios.create()
const successHandler = (res) => {}
const errorHandler = (err) => {
if (error.response.status === 401) {
// 跳转登录页
}
else if (error.response.status === 403) {
// 跳转权限页
}
else if (error.response.status === 500) {
// 跳转错误页
}
else {
// ....
}
}
request.interceptor.response.use(successHandler, errorHandler)
另一个商城项目也需要对 axios 网络请求库进行封装,这个项目对于不同响应码,业务逻辑有所改变。当接收到401状态码时,如果位于商品详情页面,需要弹出对话框进行登录,其它页面则是跳转到登录页进行登录。
// request.ts
const request = axios.create()
const successHandler = (res) => {}
const errorHandler = (err) => {
if (error.response.status === 401) {
if (isProductDetailPage()) {
// 在产品详情页面弹出对话框进行登录
} else {
// 其它页面跳转登录页面进行登录
}
}
else {
// ....
}
}
request.interceptor.response.use(successHandler, errorHandler)
我们封装的网络请求库内部掺杂不同的业务逻辑,无法复用。
如下代码是一个 EventEmitter 类,用于发布订阅。
// eventEmitter.ts
// 事件名称
const eventNames = [ 'API:401', 'API:403', 'API:500' ] as const
// 下面的类型定义相当于: type EventName = 'API:401' | 'API:403' | 'API:500'
type EventName = typeof eventNames[number]
class EventEmitter {
// 事件监听器,一个事件可以有多个监听器(处理函数)
private listeners: Record<EventName, Function[]> = {
'API:401': [],
'API:403': [],
'API:500': [],
}
/**
* 注册事件
*/
public on(eventName: EventName, listener: Function) {
this.listeners[eventName].push(listener)
}
/**
* 触发事件
*/
public emit(eventName: EventName, ...args: any[]) {
this.listeners[eventName].forEach(listener => {
listener(...args)
})
}
}
export default new EventEmitter()
改写上面的网络请求库,接收不同的响应码,触发对应的事件,完全不需要关心具体的具体的业务逻辑是什么!
// request.ts
import eventEmitter from './eventEmitter'
const request = axios.create()
const successHandler = (res) => {}
const errorHandler = (err) => {
if (error.response.status === 401) {
eventEmitter.emit('API:401')
}
else if (error.response.status === 403) {
eventEmitter.emit('API:403')
}
else if (error.response.status === 500) {
eventEmitter.emit('API:500')
}
else {
// ....
}
}
request.interceptor.response.use(successHandler, errorHandler)
经过修改后的网络请求库与业务逻辑解耦合,可以考虑发布成一个 npm 包在不同的项目中使用,比如包的名字叫做 dkvirus-request
,其中包含 request.ts 和 eventEmitter.ts 两个文件。
在项目1的入口文件中定义事件的具体实现逻辑:
import { eventEmitter } from 'dkvirus-request'
// 定义监听函数
eventEmitter.on('API:401', () => {
// 跳转登录页面
})
eventEmitter.on('API:403', () => {
// 跳转权限页面
})
eventEmitter.on('API:500', () => {
// 跳转错误页面
})
在项目2的入口文件中定义事件的具体实现逻辑:
import { eventEmitter } from 'dkvirus-request'
// 定义监听函数
eventEmitter.on('API:401', () => {
if (isProductDetailPage()) {
// 在产品详情页面弹出对话框进行登录
} else {
// 其它页面跳转登录页面进行登录
}
})
上面贴了 eventEmitter 的简化版代码,实际开发中我们不需要每次都写这个类,npm 仓库里有一个叫做 events 的第三方包,可以直接使用。感兴趣的可以看下它的 源码。
↶ 返回首页 ↶