preface

This series is a collection of handwritten functions that the front-end needs to master. The hand-tearing of these functions requires front-end developers to have certain basic skills, which will also appear frequently in the interview. The author will present each handwriting function as a separate paper, which is as meticulous as possible without making the paper too long and messy. This paper is the first handwriting function, and handwriting realizes Event Bus/ Event Emitter.

EventBus in the framework

When I first came into contact with the concept of EventBus, I used EventBus to implement cross-component communication. When A and B components were far apart but wanted to communicate, the parent props and parent $emit could not be used in this scenario. So the EventBus approach was introduced, and here’s a quick review of the use of EventBus in Vue

Create an EventBus (essentially an instance object of Vue)

    const EventBus = new Vue()
    export default EventBus
Copy the code

Mount EventBus globally

    import bus from "File path for EventBus"
    Vue.prototype.bus = bus
Copy the code

Three: Subscribe to events

    this.bus.$on('someEvent',func)
Copy the code

Four: release events

    this.bus.$emit('someEvent',params)
Copy the code

Through these four steps, we can realize communication between remote components in Vue. Components that cannot communicate directly will communicate with each other through an intermediate bridge (EventBus).

Or if you’ve used React for front-end development, you’ve probably used the EventEmitter library, which we’ll briefly review

import React, { Component } from 'react'
//一: 导入EventEmitter
import { EventEmitter } from 'events';

// Two: Build the event instance
const EventBus = EventEmitter()

// Tips: We can add a subscription to the same event in multiple components. This is just an example
class Observer extends Component {
  componentDidMount () {
    // Add event subscriptions
    this.event1 = EventBus.addListener("someEvent".(params) = > {
      console.log(params)
    })
  }
  componentWillUnMount () {
    // Remove the event subscription
    EventBus.removeListener(this.event1)
  }
  render () {
    return (
      <div>Event listener component</div>)}}class Publisher extends Component {
  handleClick () {
    const params = {}
    // 5: publish event (when someEvent is published, the function subscribes to that event is executed)
    EventBus.emit('someEvent', params)
  }
  render () {
    return (
      <div>
        <button onClick={this.handleClick.bind(this)}>Publish event</button>
      </div>)}}Copy the code

These are EventBus or Event Emitter that we use in the framework. Their functions are to enable components of remote relationships to complete information delivery by themselves, which is generally called global Event bus

The design of the global event bus uses one of the most important design patterns of the front end, the publish-subscribe mode, which is generally used in the following scenarios:

  1. Components or things that need to communicate, such as change notifications, do not or cannot communicate directly (not aware of each other), but are mediated by a third party
  2. There is a one-to-many relationship between publisher and subscriber. One publisher can correspond to multiple subscribers. When a publisher publishes information, all subscribers who subscribe to this information respond

Supplement: If you have learned about the observer pattern, you will find the similar to the observer pattern, but in fact the existence difference, the key lies in whether there is a third party Bridges, the observer pattern is the observer and the observed direct communication, to mutual perception, both in their logical coupling (said this coupling is not bad, But because there is a functional relationship between the two), and the publish subscription model is that subscribers and publishers do not need to communicate directly, they do not need to perceive each other, through the third party to assist communication.

After these examples, you can get a sense of how Event Emitter works, even if you haven’t used it before. Now let’s implement a simplified version of Event Emitter

Implementation of EventBus/Event Emitter

The basic implementation of Event Emitter is as follows

class EventEmitter {
  constructor() {
    // Singleton mode
    if(! EventEmitter.instance) { EventEmitter.instance =this
      this.handleMap = {}
    }
    // Map structure, used to store events and their corresponding callbacks
    return EventEmitter.instance
  }

  // To subscribe to events, you need to receive the subscription event name and the corresponding callback function
  on (eventName, callback) {
    this.handleMap[eventName] = this.handleMap[eventName] ?? []
    this.handleMap[eventName].push(callback)
  }

  // Event publish, need to receive publish event name and corresponding parametersemit (eventName, ... args) {if (this.handleMap[eventName]) {
      // We need to make a shallow copy of handleMap[eventName], because this. HandleMap will be modified when once adds the subscription. If once is bound first, the last listener will be removed
      const handlers = [...this.handleMap[eventName]]
      handlers.forEach(callback= >callback(... args)) } }// Remove the subscription, the name of the subscription event to remove and the specified callback function
  remove (eventName, callback) {
    const callBacks = this.handleMap[eventName]
    const index = callBacks.indexOf(callback)
    if(index ! = = -1) {
      callBacks.splice(index, 1)}}// Add a single subscription, trigger a subscription event to cancel the subscription, the need to add the subscription event name and the specified callback function
  once (eventName, callback) {
    const warpper = (. args) = >{ callback(... args)this.remove(eventName, warpper)
    }
    this.on(eventName, warpper)
  }
}

// Basic tests
const eventBus = new EventEmitter()
eventBus.once("demo".(params) = > { console.log(1, params) })
eventBus.on("demo".(params) = > { console.log(2, params) })
eventBus.on("demo".(params) = > { console.log(3, params) })
eventBus.emit("demo"."someData")
Copy the code

The above is a simple implementation of Event Emitter using publish and subscribe mode, which implements four methods of on, EMIT, once and remove respectively. For those who know the above, you can check the source of Event Emitter library, which is a small library with few contents. It would be instructive to look at how the same functionality is implemented in this widely used class library.

conclusion

In front-end development, we will use many robust wheels that have been packaged by others. While feeling convenient, we will also try to understand and study the design ideas involved. The reason for such design is that the basic package of Event Emitter is simply sorted out here.

We can associate the publish and subscribe mode with the observer mode, which is used in the responsive design of Vue. Interested students can learn about this mode, and the author will also sort out this knowledge point and link it here.