写一个简单的
事件订阅中心 eventsHub
当两个组件需要互相通信时,可以让它们通过一个共享的事件中心交换消息。本文沿着事件存储、订阅、发布的顺序,用原生 JavaScript 梳理这一过程。
阅读线索
订阅方用 on 登记回调,发布方用 emit 发出数据。事件中心根据相同的事件名,把两端连接起来。
01 创建事件容器
先准备一个 eventsHub 对象,再用 events 保存事件。每个事件名对应一个回调数组,同一个事件可以被多个组件订阅。
const eventsHub = {
// 用无原型对象存储,避免事件名与内置属性冲突。
events: Object.create(null)
};02 绑定事件:on
on(name, callback) 接收事件名和回调函数。第一次订阅时创建数组,后续订阅则继续追加回调。为便于定位调用错误,这里同时检查事件名和回调类型。
eventsHub.on = function (name, callback) {
if (typeof name !== 'string' || !name.trim()) {
throw new TypeError('eventsHub.on:事件名必须是非空字符串');
}
if (typeof callback !== 'function') {
throw new TypeError('eventsHub.on:回调必须是函数');
}
if (!this.events[name]) this.events[name] = [];
this.events[name].push(callback);
};03 发布事件:emit
emit(name, data) 查找对应的回调列表,依次把数据传给订阅方。没有订阅者时直接结束;执行前复制数组,避免本次回调中新添加的订阅参与当前发布。
eventsHub.emit = function (name, data) {
if (typeof name !== 'string' || !name.trim()) {
throw new TypeError('eventsHub.emit:事件名必须是非空字符串');
}
const callbacks = this.events[name];
if (!callbacks) return;
// 当前发布只执行开始时已登记的订阅。
callbacks.slice().forEach((callback) => callback(data));
};04 把两个组件连接起来
将上面三个代码片段依次放入同一脚本后,就可以先订阅,再发布。下面的订阅方把收到的消息写到页面元素中,发布方只需要知道约定的事件名。
<p id="message">等待接收消息</p>
<script>
// 先在此脚本之前定义上文的 eventsHub、on 和 emit。
// 组件 B:订阅消息,将数据安全地写入文本节点。
eventsHub.on('message:update', (data) => {
const output = document.getElementById('message');
if (output) output.textContent = data.message;
});
// 组件 A:发布消息,可以传递一个对象。
eventsHub.emit('message:update', {
message: '组件之间的消息已经送达。'
});
</script>使用时留意
订阅应先于发布;事件名必须一致。该示例同步执行回调,回调抛错会中断本次发布;实际项目还需根据组件生命周期补充取消订阅,避免重复绑定。
SOURCE & REFERENCE
这篇笔记的来源
根据 Ask_Js 的原文整理,保留事件订阅与发布的核心思路,并为示例补充了输入检查和事件名边界处理。
阅读原文:写一个简单的事件订阅中心 eventsHub