javascript - DOM 之外的浏览器 Javascript 中的自定义事件发射器/消费者

标签 javascript browser

目前我有以下结构,一个对象 WebsocketService 和多个 FrontendController

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

为了让我的 UI 代码和应用程​​序逻辑很好地分离,我设想了一种设计,其中 FrontendController 可以订阅 WebsocketService 发出的事件,很可能稍后还有其他服务.

我已阅读 Creating and triggering events on MDN 的文档.但所有这些都是以 DOM 为中心的。我的 WebsocketService 显然没有 addEventListener 函数。

我宁愿不使用第 3 方库。我想到的一个 hack 就是在某个地方有一个 id="events" DOM 节点并让每个人都订阅它。然后 dispatchEvent 在该元素上,并让 FrontendController 决定他们关心。

当然我也可以为所有这些实现我自己的基础设施,但我也宁愿避免那样做,因为我很懒。

执行此操作的干净方法是什么?或者是否有更好的解决方案来分离我的应用程序逻辑和普通旧 javascript 中的前端?

最佳答案

我认为最简单的可能性是:

  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. 实现您自己的精简事件机制

//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();

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

关于javascript - DOM 之外的浏览器 Javascript 中的自定义事件发射器/消费者,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51110935/

相关文章:

image - 图片的默认过期时间

R:如果已经加载,则重新加载 google chrome URL

css - 为什么浏览器 devtools 中计算的字体大小与我的样式表值不同?

javascript - 确定元素是否在另一个元素后面

javascript - 为什么 Chrome 控制台中的 {} + {} 不再是 NaN?

javascript - 如何将多个值推送到表单中?

javascript - 如何应用向 Ramda 中的每个列表项返回 promise 的函数

javascript - Node.js 插件中的回调函数

javascript - 如何检测 $ 函数背后的库是什么?

javascript - "Zoom"使用 JavaScript 的浏览器窗口/ View ?