Creemos un bus de eventos nativo ligero en JavaScript

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 idioma) que se puede utilizar para simplificar las comunicaciones entre diferentes componentes. También se puede considerar como publicación/suscripción 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” podría escuchar eventos que le indiquen que cambie la pestaña activa. Claro, eso podría suceder al hacer clic en una de las pestañas y, por lo tanto, manejarse 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 del que se debe alertar al usuario dentro de una pestaña específica, por lo que el formulario envía un mensaje al bus de eventos indicándole al componente de pestañas que cambia la pestaña activa a la que tiene el error. Así es como se ve a bordo de un autobús de eventos.
El pseudocódigo para esa situación sería como…
// Tab ComponentTabs.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 JavaScript para esto? (Pregunta capciosa: nunca necesitarás una biblioteca de JavaScript). Bueno, hay muchas opciones disponibles:
- PubSubJS
- EventEmitter3
- Postal.js
- jQuery incluso admitiría eventos personalizados, lo cual está muy relacionado con este patrón.
Además, consulta Mitt, que es una biblioteca que tiene solo 200 bytes comprimidos. 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 utilizaremos ninguna biblioteca de terceros y aprovecharemos un sistema de escucha de eventos que ya está integrado en JavaScript con el sistema que addEventListener
todos conocemos y amamos.
Primero, un poco de contexto.
La addEventListener
API en JavaScript es una función miembro de la EventTarget
clase. La razón por la que podemos vincular un click
evento a un botón es porque el prototipo de interfaz de button
( HTMLButtonElement
) hereda EventTarget
indirectamente.
A diferencia de la mayoría de las otras interfaces DOM, EventTarget
se puede crear directamente usando la new
palabra clave. Es compatible con todos los navegadores modernos, pero sólo recientemente. Como podemos ver en la captura de pantalla anterior, Node
hereda EventTarget
, por lo que todos los nodos DOM tienen método addEventListener
.
Aquí está el truco
Estoy sugiriendo un Node
tipo extremadamente liviano para que actúe 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 otra funcionalidad que el texto descriptivo para los desarrolladores. Pero como los comentarios todavía se escriben en HTML, terminan en el DOM como nodos reales y tienen su propio prototipo de interfaz, Comment
que hereda Node
.
La Comment
clase se puede crear new
directamente como EventTarget
puede:
const myEventBus = new Comment('my-event-bus');
También podríamos utilizar la document.createComment
API antigua, pero ampliamente compatible. 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
campo se puede utilizar para contener cualquier dato.
myEventBus.dispatchEvent( new CustomEvent('event-name', { detail: 'event-data' }));
Internet Explorer 9-11 es compatible CustomEvent
, pero ninguna de las versiones lo es new CustomEvent
. Es complejo simularlo usando document.createEvent
, por lo que si la compatibilidad con IE es importante para usted, hay una manera de realizar un polyfill.
Ahora podemos vincular detectores de eventos:
myEventBus.addEventListener('event-name', ({ detail }) = { console.log(detail); // = event-data});
Si un evento pretende activarse solo una vez, podemos usarlo { once: true }
para vinculación única. Otras opciones no encajarán aquí. Para eliminar detectores de eventos, podemos usar el archivo nativo removeEventListener
.
Depuración
La cantidad de eventos vinculados a un bus de eventos único puede ser enorme. También puede haber pérdidas de memoria si olvida eliminarlas. ¿Qué pasa si queremos saber a cuántos eventos están vinculados 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úrese de desmarcar “Ancestros” para ocultar los eventos vinculados a document
y window
.
un ejemplo
Un inconveniente es que la sintaxis de EventTarget
es ligeramente detallada. Podemos escribir un contenedor simple para ello. Aquí hay una demostración en TypeScript a continuación:
class EventBusDetailType = any { private eventTarget: EventTarget; constructor(description = '') { this.eventTarget = document.appendChild(document.createComment(description)); } on(type: string, listener: (event: CustomEventDetailType) = void) { this.eventTarget.addEventListener(type, listener); } once(type: string, listener: (event: CustomEventDetailType) = void) { this.eventTarget.addEventListener(type, listener, { once: true }); } off(type: string, listener: (event: CustomEventDetailType) = void) { this.eventTarget.removeEventListener(type, listener); } emit(type: string, detail?: DetailType) { return this.eventTarget.dispatchEvent(new CustomEvent(type, { detail })); }} // Usageconst myEventBus = new EventBusstring('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 HellomyEventBus.emit('event-name', 'World'); // = World
La siguiente demostración proporciona el JavaScript compilado.
¡Y ahí lo tenemos! Acabamos de crear un bus de escucha de eventos libres de dependencias donde un componente puede informar a otro componente sobre los cambios para desencadenar una acción. No se necesita una biblioteca completa para hacer este tipo de cosas y las posibilidades que abren son infinitas.
Deja un comentario