阅读线索

订阅方用 on 登记回调,发布方用 emit 发出数据。事件中心根据相同的事件名,把两端连接起来。

01 创建事件容器

先准备一个 eventsHub 对象,再用 events 保存事件。每个事件名对应一个回调数组,同一个事件可以被多个组件订阅。

JavaScript
const eventsHub = {
  // 用无原型对象存储,避免事件名与内置属性冲突。
  events: Object.create(null)
};

02 绑定事件:on

on(name, callback) 接收事件名和回调函数。第一次订阅时创建数组,后续订阅则继续追加回调。为便于定位调用错误,这里同时检查事件名和回调类型。

JavaScript
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) 查找对应的回调列表,依次把数据传给订阅方。没有订阅者时直接结束;执行前复制数组,避免本次回调中新添加的订阅参与当前发布。

JavaScript
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 把两个组件连接起来

将上面三个代码片段依次放入同一脚本后,就可以先订阅,再发布。下面的订阅方把收到的消息写到页面元素中,发布方只需要知道约定的事件名。

HTML + JavaScript
<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
回到顶部