Creemos un bus de eventos nativo ligero en JavaScript | Programar Plus

Un bus de eventos es un patrón de diseño (y aunque aquí hablaremos de JavaScript, es un patrón de diseño en cualquier lenguaje) que se puede utilizar para simplificar las comunicaciones entre diferentes componentes. También se puede considerar publicar / suscribir o pubsub.

La idea es que los componentes puedan escuchar el bus de eventos para saber cuándo hacer las cosas que hacen. Por ejemplo, un componente de “panel de pestañas” puede escuchar eventos que le indiquen que cambie la pestaña activa. Claro, eso puede suceder con un clic en una de las pestañas y, por lo tanto, se maneja completamente dentro de ese componente. Pero con un bus de eventos, algunos otros elementos podrían indicarle a la pestaña que cambie. Imagine el envío de un formulario que provoca un error que debe alertar al usuario dentro de una pestaña específica, por lo que el formulario envía un mensaje al bus de eventos diciéndole al componente de pestañas que cambie la pestaña activa a la que tiene el error. Eso es lo que parece a bordo de un autobús de eventos.

El pseudocódigo para esa situación sería como …

// Tab Component
Tabs.changeTab = id => {
  // DOM work to change the active tab.
}
MyEventBus.subscribe("change-tab", Tabs.changeTab(id));

// Some other component...
// something happens, then:
MyEventBus.publish("change-tab", 2);  

¿Necesita una biblioteca de JavaScript para esto? (Pregunta capciosa: nunca necesitas una biblioteca de JavaScript). Bueno, hay muchas opciones disponibles:

  • PubSubJS
  • EventEmitter3
  • Postal.js
  • jQuery incluso admitía eventos personalizados, que está muy relacionado con este patrón.

Además, echa un vistazo a Mitt, que es una biblioteca que solo tiene 200 bytes comprimidos en gzip. Hay algo en este patrón simple que inspira a las personas a abordarlo ellos mismos de la manera más sucinta posible.

¡Hagámoslo nosotros mismos! No usaremos ninguna biblioteca de terceros y aprovecharemos un sistema de escucha de eventos que ya está integrado en JavaScript con el addEventListener todos conocemos y amamos.

Primero, un poco de contexto

El addEventListener API en JavaScript es una función miembro de la EventTarget clase. La razón por la que podemos unir un click evento a un botón se debe a que la interfaz prototipo de <button> (HTMLButtonElement) hereda de EventTarget indirectamente.

Fuente: MDN Web Docs

A diferencia de la mayoría de las otras interfaces DOM, EventTarget se puede crear directamente usando el new palabra clave. Es compatible con todos los navegadores modernos, pero solo recientemente. Como podemos ver en la captura de pantalla anterior, Node hereda EventTarget, por lo que todos los nodos DOM tienen método addEventListener.

Aqui esta el truco

Sugiero un extremadamente ligero Node escriba para actuar como nuestro bus de escucha de eventos: un comentario HTML (<!-- comment -->).

Para un motor de renderizado de navegador, los comentarios HTML son solo notas en el código que no tienen más funcionalidad que el texto descriptivo para los desarrolladores. Pero como los comentarios todavía están escritos en HTML, terminan en el DOM como nodos reales y tienen su propia interfaz prototipo:Comment—Que hereda Node.

El Comment la clase se puede crear a partir de new directamente como EventTarget poder:

const myEventBus = new Comment('my-event-bus');

También podríamos usar el antiguo, pero ampliamente respaldado document.createComment API. Requiere un data parámetro, que es el contenido del comentario. Incluso puede ser una cadena vacía:

const myEventBus = document.createComment('my-event-bus');

Ahora podemos emitir eventos usando dispatchEvent, que acepta un Event Objeto. Para pasar datos de eventos definidos por el usuario, utilice CustomEvent, donde el detail El campo se puede utilizar para contener cualquier dato.

myEventBus.dispatchEvent(
  new CustomEvent('event-name', { 
    detail: 'event-data'
  })
);

Compatible con Internet Explorer 9-11 CustomEvent, pero ninguna de las versiones admite new CustomEvent. Es complejo simularlo usando document.createEvent, por lo que si el soporte de IE es importante para usted, hay una manera de rellenarlo.

Ahora podemos vincular a los oyentes de eventos:

myEventBus.addEventListener('event-name', ({ detail }) => {
  console.log(detail); // => event-data
});

Si un evento tiene la intención de activarse solo una vez, podemos usar { once: true } para encuadernación única. Otras opciones no encajarán aquí. Para eliminar los detectores de eventos, podemos usar el nativo removeEventListener.

Depuración

La cantidad de eventos vinculados a un bus de evento único puede ser enorme. También puede haber pérdidas de memoria si se olvida de eliminarlas. ¿Qué pasa si queremos saber cuántos eventos están vinculados a myEventBus?

myEventBus es un nodo DOM, por lo que DevTools puede inspeccionarlo en el navegador. Desde allí, podemos encontrar los eventos en la pestaña Elementos → Escuchas de eventos. Asegúrate de desmarcar “Ancestros” para ocultar los eventos vinculados document y window.

Un ejemplo

Un inconveniente es que la sintaxis de EventTarget es un poco detallado. Podemos escribir una envoltura simple para él. Aquí hay una demostración en TypeScript a continuación:

class EventBus<DetailType = any> {
  private eventTarget: EventTarget;
  constructor(description = '') { this.eventTarget = document.appendChild(document.createComment(description)); }
  on(type: string, listener: (event: CustomEvent<DetailType>) => void) { this.eventTarget.addEventListener(type, listener); }
  once(type: string, listener: (event: CustomEvent<DetailType>) => void) { this.eventTarget.addEventListener(type, listener, { once: true }); }
  off(type: string, listener: (event: CustomEvent<DetailType>) => void) { this.eventTarget.removeEventListener(type, listener); }
  emit(type: string, detail?: DetailType) { return this.eventTarget.dispatchEvent(new CustomEvent(type, { detail })); }
}
    
// Usage
const myEventBus = new EventBus<string>('my-event-bus');
myEventBus.on('event-name', ({ detail }) => {
  console.log(detail);
});

myEventBus.once('event-name', ({ detail }) => {
  console.log(detail);
});

myEventBus.emit('event-name', 'Hello'); // => Hello Hello
myEventBus.emit('event-name', 'World'); // => World

La siguiente demostración proporciona JavaScript compilado.

¡Y ahí lo tenemos! Acabamos de crear un bus de escucha de eventos libre de dependencias donde un componente puede informar a otro componente de los cambios para desencadenar una acción. No se necesita una biblioteca completa para hacer este tipo de cosas, y las posibilidades que abre son bastante infinitas.

(Visited 5 times, 1 visits today)