欢迎光临
我们一直在努力

发布订阅模式(EventEmitter)--结合生产使用

一、概述

发布订阅模式(Publish-Subscribe Pattern)是一种经典的设计模式,核心是解耦事件的发布者和订阅者,使两者无需直接依赖即可完成通信。本文档基于 TypeScript 实现了支持单页面通信的 EventEmitter,并提供完整的使用指南、API 说明和最佳实践。

核心优势

  • 发布者(emit)只管发事件,不关心谁接收
  • 订阅者(on/once)只管监听事件,不关心谁发送
  • 适合:组件通信、异步通知、业务解耦
  • 二、核心功能

    方法说明
    on(name, fn) 订阅事件(持久)
    once(name, fn) 订阅一次(执行后自动销毁)
    emit(name, …args) 触发事件,传参
    off(name?, fn?) 取消订阅
    listenerCount(name) 获取监听数
    eventNames() 获取所有事件名

    三、完整代码

    /**
    * 事件发射器核心类
    * 实现事件的订阅、取消订阅、触发、一次性订阅等功能
    */

    class EventEmitter {
    /* 存储事件名与对应回调函数数组的映射,增强类型约束 */
    private events:{[key:string]:Function[]}={}
    /* 订阅事件 */
    on(eventName:string ,callback:Function):this {
    if(!this.events[eventName]){
    this.events[eventName] = []
    }
    this.events[eventName].push(callback)
    return this
    }
    /* 一次性订阅 */
    once(eventName:string,callback:Function):this{
    const onceCallback = (…args:any[])=>{
    callback(…args)
    this.off(eventName,onceCallback)
    }
    return this.on(eventName,onceCallback)
    }
    /* 取消订阅 */
    off(eventName?:string ,callback?:Function):this{
    if(!eventName){
    /* 清除所有事件 */
    this.events = {}
    return this
    }
    if(!callback){
    /* 清除指定事件的所有回调 */
    delete this.events[eventName]
    return this
    }
    /* 移除指定回调 */
    const callbacks = this.events[eventName]
    if(callbacks){
    const index = callbacks.indexOf(callback)
    if(index>-1){
    callbacks.splice(index,1)
    }
    }
    return this
    }
    /* 触发事件 */
    emit(eventName:string,…args:any[]):this{
    const callbacks = this.events[eventName]
    if(callbacks){
    callbacks.forEach(callback=>{
    try {
    callback(…args)
    } catch (error) {
    console.log(`Error in event ${eventName}:`,error)
    }
    })
    }
    return this
    }
    /* 获取事件监听器数量 */
    listenerCount(eventName:string):number{
    return this.events[eventName]?.length||0
    }
    /* 获取所有事件名 */
    eventNames():string[]{
    return Object.keys(this.events)
    }
    }

    /* eventCenter (单例 + DOM风格API)*/

    class EventCenter extends EventEmitter {
    addEventListener(eventName:string,callback:Function):this{
    return this.on(eventName,callback)
    }
    removeEventListener(eventName:string,callback?:Function):this{
    return this.off(eventName,callback)
    }
    dispatchEvent(eventName:string,data?:any):this{
    return this.emit(eventName,data)
    }
    }

    /* 创建单例实例 */
    const eventCenter = new EventCenter()

    export default eventCenter

    使用示例:

    import eventCenter from './simple-event-center'

    // 订阅事件
    const handler = (data) => {
    console.log('收到消息:', data)
    }
    eventCenter.addEventListener('message', handler)

    // 触发事件
    eventCenter.dispatchEvent('message', { text: 'Hello World' })

    // 一次性订阅
    eventCenter.once('once-event', (data) => {
    console.log('只执行一次:', data)
    })

    // 取消订阅
    eventCenter.removeEventListener('message', handler)

    四、注意点

    本方法在跨浏览器标签页 / 窗口无法直接正常使用,仅支持单页面使用

    核心原因:每个浏览器标签页 / 窗口都是独立的 JavaScript 执行环境,你的 eventCenter 是单个页面内的单例对象,不同页面的 eventCenter 内存空间完全隔离 ——A 页面发布的事件,B 页面的 eventCenter 根本接收不到。

    举个直观例子:

    • 标签页 1:eventCenter.emit('user:login', {name: '张三'}) → 只有标签页 1 内的订阅者能收到;
    • 标签页 2:eventCenter.on('user:login', (data) => console.log(data)) → 永远收不到标签页 1 的事件。

    想要实现跨页面通信:

    方案核心 API适用场景优点缺点
    1. localStorage + storage 事件 localStorage.setItem + window.addEventListener('storage') 同域名下的跨标签页 简单、无需处理跨域、兼容性好 只能同域名、数据存在本地
    2. postMessage window.postMessage 跨域名 /iframe/ 窗口 支持跨域、灵活 需要获取目标窗口的引用
    赞(0)
    未经允许不得转载:171主机测评 » 发布订阅模式(EventEmitter)--结合生产使用
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址