事件订阅的几种实现风格

开发 前端
三种风格,第一种是监听器本身作为标识,第二种是使用额外的一个 id(数字或字符串)来代表监听器,第三种是在调用监听方法时,直接返回一个可以取消监听的函数。

大家好,我是前端西瓜哥。

事件订阅是模块间解耦的常见方式。

比如相隔遥远的两个组件,可以通过一个订阅,一个发布的方式,实现数据通信。

下面我们来看看事件订阅的几种设计风格。

监听器函数

第一种是 使用监听器函数本身作为标识符。

常见的场景有 DOM 事件的绑定:

const handler = () {
// do something
}

window.addEventListener('resize', handler); // 绑定事件
window.removeEventListener('resize', handler); // 取消事件

此外还有 Node.js 的 EventEmitter 类,很多支持监听事件的类都继承了它。用法为:

import { EventEmitter } from "node:events";

const myEmitter = new EventEmitter();
const handler = () {
console.log("前端西瓜哥");
};

myEmitter.on("event", handler); // 绑定事件
myEmitter.emit("event"); // 打印了内容

myEmitter.off("event", handler); // 取消事件
myEmitter.emit("event"); // 无事发生

原理很简单,就是维护一个映射表上,key 为事件名,value 为要顺序执行的监听器,大概这样:

{
'resize': [handler1, handler2],
'click': [handler3, handler4]
}

一个监听器函数就是一个唯一的对象,通过它可以找出在对应事件下的位置,将其从列表中移除,就算是取消了事件绑定。

取消绑定逻辑大概为:

const index = map[eventName].indexOf(handler);
if (index !== -1) {
map[eventName].splice(index, 1);
}

订阅 id

通过一个 id 来代表绑定的监听器。

经典场景为 setTimeout:

const timeoutId = setTimeout(() {
// ...
}, 1000); // 订阅

clearTimeout(timeoutId); // 取消

实现原来基本类似前一种方式,只是改为用 id 来作为标识。

{
[eventName]: [
{id: 1, hander: handler1},
{id: 2, hander: handler2},
]
}

返回封装好的取消绑定方法

上面两种写法,都需要一个变量额外保存标识,然后再使用事件订阅对象专门的取消订阅函数,难免有点繁琐。

我们对第一种风格,可以做一个封装:

const bindEvent = (target, eventName, handler) => {
target.addEventListener('resize', handler); // 绑定
return () {
target.removeEventListener('resize', handler); // 取消
}
}

const unBindEvent = bindEvent(window, 'resize', handler); // 封装的绑定
unBindEvent(); // 封装的取消

这种写法直接调用返回的函数即可解绑,不需要保存 id 和一个专门的取消订阅函数,代码更简洁。

常见场景是 React 的 useEffect:

const App = () {
useEffect(() {
const handler = () {
// ...
}
window.addEventListener('resize', handler); // 绑定事件
return () {
window.removeEventListener('resize', handler); // 取消事件
}
}, [])
}

结尾

三种风格,第一种是监听器本身作为标识,第二种是使用额外的一个 id(数字或字符串)来代表监听器,第三种是在调用监听方法时,直接返回一个可以取消监听的函数。

责任编辑:姜华 来源: 前端西瓜哥
相关推荐

2023-08-14 14:04:14

JavaScript函数式编程

2021-08-16 07:26:42

服务订阅机制

2024-02-29 08:21:05

MediatR.NET库传递消息

2009-12-23 16:06:46

WPF事件策略

2009-07-29 17:44:02

ibmdwJava

2022-12-02 07:28:58

Event订阅模式Spring

2023-02-08 08:43:55

前端继承原型

2021-10-07 20:36:45

Redis集群场景

2009-09-18 12:29:55

2023-02-07 07:25:36

Spring事件驱动

2010-08-05 09:39:17

Flex页面跳转

2011-06-28 16:40:17

Qt Widget 图片

2012-04-10 10:49:45

WEBCSS

2009-08-31 18:01:41

C#接口事件

2023-10-23 10:43:24

SpringRestful风格

2009-12-02 20:02:18

PHP实现页面跳转

2010-04-30 16:22:07

Unix终端

2022-05-19 14:14:26

go语言限流算法

2020-05-08 11:13:28

Python数据技术

2009-09-09 11:24:46

PHP实现MVC
点赞
收藏

51CTO技术栈公众号