首页 > 解决方案 > DOM外的浏览器Javascript中的自定义事件发射器/消费者

问题描述

目前我有以下结构,一个 ObjectWebsocketService和多个FrontendControllers。

WebsocketService侦听 websocket (duh) 并更新内部应用程序状态。这意味着在某些情况下必须使用新值更新前端。

为了使我的 UI 代码和应用程​​序逻辑很好地分离,我设想了一种设计,其中FrontendControllers 可以订阅事件,WebsocketService并且很可能稍后会订阅其他服务。

我已阅读有关在 MDN 上创建和触发事件的文档。但所有这些都非常以 DOM 为中心。我的WebsocketService显然没有addEventListener功能。

我宁愿不使用第 3 方库。想到的一个技巧就是在id="events"某个地方有一个 DOM 节点并让每个人都订阅它。然后dispatchEvents 在那个 Element 上,让FrontendControllers 决定他们关心的问题。

当然,我也可以为所有这些实现我自己的基础设施,但我也更愿意避免这种情况,因为我很懒。

这样做的干净方法是什么?或者有没有更好的解决方案来解耦我的应用程序逻辑和前端在普通的旧 javascript 中?

标签: javascriptbrowser

解决方案


我看到的简单可能性是:

  1. 让你的 WebsocketService 扩展 HTMLElement

    好处:

    • 简单的
    • 您继承事件相关的方法,包括 addEventListener 和 dispatchEvent。

    缺点:

    • 你继承了所有你永远不会使用的 DOM 方法和属性。
    • 部分支持(FireFox 需要使用简单但特定的配置运行,Edge/Explorer 不支持)

    样本:

//add some event
let piggyEvent = document.createEvent('Event');
piggyEvent.initEvent('piggy', true, true);

class WebSocketService extends HTMLElement {
  constructor() {
    super();
  }
  onDataArrival() {
    //some code here...
  }
  onConnectionLoss() {
    //some code here...
  }
  onHappyPiggyArrival() {
    this.dispatchEvent(piggyEvent);
  }
}

class FrontEndService {
  constructor(socketService) {
    this.socketService = socketService;
  }
  subscribe() {
    this.socketService.addEventListener('piggy', (event) => {
      alert("A new piggy has arrived, do something with it!");
    })
  }
}

//You must declare the new element
customElements.define('websocket-service', WebSocketService);

//Now let's try it
let socService = new WebSocketService(),
  frontService = new FrontEndService(socService);
frontService.subscribe();

//nothing happens until...
socService.onHappyPiggyArrival();

  1. 实现自己的 addEventListener 方法并使用现有机制
  2. 实现自己的 lite 事件机制

//add some event
let piggyEvent = document.createEvent('Event');
piggyEvent.initEvent('piggy', true, true);


class EventSupportedElement {
  constructor() {
    this.eventRegsitry = new Map();
  }

  _initRegEntry(entryName) {
    if (!this.eventRegsitry.has(entryName)) {
      this.eventRegsitry.set(entryName, []);
    }
    return this.eventRegsitry.get(entryName);
  }

  addEventListener(eventName, callback) {
    this._initRegEntry(eventName).push(callback);
  }

  removeEventListener(eventName, callback) {
    let eventNameCallbacks = this.eventRegsitry.get(eventName);
    if (eventNameCallbacks && eventNameCallbacks.indexOf(callback) !== -1) {
      eventNameCallbacks.splice(eventNameCallbacks.indexOf(callback), 1);
    }
  }

  dispatchEvent(eventObject) {
    if (eventObject.type && this.eventRegsitry.get(eventObject.type)) {
      this.eventRegsitry.get(eventObject.type).forEach((callback) => {
        //add any data to the event / use custom event and trigger
        callback(eventObject);
      });
    }
  }
}

class WebSocketService extends EventSupportedElement {
  onDataArrival() {
    //some code here...
  }

  onConnectionLoss() {
    //some code here...
  }

  onHappyPiggyArrival() {
    this.dispatchEvent(piggyEvent);
  }
}

class FrontEndService {
  constructor(socketService) {
    this.socketService = socketService;
  }

  subscribe(eventName, callback) {
    this.socketService.addEventListener(eventName, callback);
  }
  unsubscribe(eventName, callback) {
    this.socketService.removeEventListener(eventName, callback);
  };
}

//Now let's try it
let socService = new WebSocketService(),
  frontService = new FrontEndService(socService),
  onPiggyFn = (event) => {
    alert("A new piggy has arrived, do something with it!");
  };
frontService.subscribe('piggy', onPiggyFn);

//nothing happens until...
socService.onHappyPiggyArrival();

//now unsubscribe
frontService.unsubscribe('piggy', onPiggyFn);

//nothing happens...
socService.onHappyPiggyArrival();

无论您选择哪一个,您都必须声明自己的自定义事件。


推荐阅读