发布订阅模式
js
class EventEmitter {
constructor() {
this.cache = {};
}
on(name, fn) {
if (this.cache[name]) {
this.cache[name].push(fn);
} else {
this.cache[name] = [fn];
}
}
off(name, fn) {
const tasks = this.cache[name];
if (tasks) {
const index = tasks.findIndex((f) => f === fn || f.callback === fn);
if (index >= 0) {
tasks.splice(index, 1);
}
}
}
emit(name, once = false) {
if (this.cache[name]) {
// 创建副本,如果回调函数内继续注册相同事件,会造成死循环
const tasks = this.cache[name].slice();
for (let fn of tasks) {
fn();
}
if (once) {
delete this.cache[name];
}
}
}
}
// 测试
const eventBus = new EventEmitter();
const task1 = () => {
console.log("task1");
};
const task2 = () => {
console.log("task2");
};
eventBus.on("task", task1);
eventBus.on("task", task2);
eventBus.off("task", task1);
setTimeout(() => {
eventBus.emit("task"); // task2
}, 1000);使用发布订阅模式解耦
在前端项目开发中,会遇到如router、axios等模块发生一些事件需要触发页面或其它模块的操作,但直接互相引用往往会使得组件之间的耦合性越来越大,逐渐堆积成屎山,因此可以在设计上采用发布订阅模式对组件进行解耦
ts
const eventNames = ['API:UN_AUTH', 'API:VALIDATE_ERROR'] as const;
type EventNames = (typeof eventNames)[number]
class EventEmitter {
private listeners: Record<EventNames, Set<Function>> = {
'API:UN_AUTH': new Set(),
'API:VALIDATE_ERROR': new Set(),
};
on(eventName: EventNames, callback: Function) {
this.listeners[eventName].add(callback);
}
emit(eventName: EventNames, ...args: any[]) {
this.listeners[eventName].forEach((callback) => callback(...args));
}
}
export default new EventEmitter();此时如在某个axios拦截其中
js
const sucessHandler = () => {
// ...
}
const errorHandler = () => {
if (error.response.status === 401) {
eventEmitter.emit('API:UN_AUTH');
}
}
axios.intercepts.response.use(sucessHandler, errorHandler);路由中
js
eventEmitter.on('API:UN_AUTH', () => {
router.push('/login'); // 跳转到登录页
})