background

In all sorts of small procedures, we often encounter this kind of situation has a list, click a enter in the list of details, details, there is a button, deleted this one, this time when a user returns to the list page, found that this one remains on the list, this kind of situation, is a ` bug `, namely data sync problem, Test at this time the little sister would be looking for you, let you to solve, this time, you may be very quickly solve, but after a while, and test the little sister said to come to you, I opened the four or five pages to change the user status, but I am one layer to return to the home page, found that there are several no refresh the page data, is also a bug, this time you come, How do you do it? How do you do it normallyCopy the code

The solution

1. Put all requests into the life cycle 'onShow', as soon as our page is displayed again, we will re-request, data will be refreshed 2. Get the page stack with 'getCurrentPages', then find the corresponding page instance and call the instance method to refresh the data 3. By setting a global variable, such as app.globaldata.xxx, change the value of this variable and then check in the corresponding onShow. If the value has changed, refresh the data 4. Use redirectTo instead of navigateTo when opening the details page. This will destroy the current page when opening a new page, and will not return to this page, so there is no data synchronization problemCopy the code

Existing problems

1. If we put all the requests in the onShow life cycle, we can solve all the data refresh problems, but there are two problems with the onShow life cycle. The first problem is that it actually executes after the onLoad. The second problem is that page hiding, calling wechat sharing, frequency locking and so on will trigger execution. Placing the request in 'onShow' will cause a large number of unnecessary requests, resulting in server pressure, waste of redundant resources, and also cause bad user experience. Get the page stack through 'getCurrentPages', then find the corresponding page instance, call the instance method to refresh the data, this is also a way, but as the official document of wechat says, > do not try to modify the page stack, it will lead to routing and page status errors. > do not call 'getCurrentPages()' at app.onlaunch before page is generated. In addition, using 'getCurrentPages' is difficult and tedious when there are two, three, or more pages to communicate with. Through the method of setting global variables, when the need to use a small number of places, it can be accepted, when the use of a lot of places, it will be difficult to maintain, the code is too bloated, there will be a lot of problems 4. Using redirectTo instead of navigateTo is terrible for the experience, and there's only one page, and it doesn't work with TAB pages, so it's not recommendedCopy the code

Best practices

In Vue, new Vue() can be used to realize an event bus as an event bus to achieve the function of event notification. In each major framework, there is also its own event mechanism. Therefore, we can completely implement an event center to manage our events through the same method. Solve our problems. Iny - Bus is an extremely lightweight event library, written in typescript with 100% test coverage, available in any environment that can run JSCopy the code

Portal source NPM document

Simple to use

Iny -bus is extremely simple to use. Add event listener to the required page onLoad and distribute events where events need to be triggered, so that each page listening to the event can execute processing functions to achieve the purpose of communication and data refresh. The following code can be used in small programsCopy the code

  / / small programs
  import bus from 'iny-bus'

  // Add event listener
  // Register in onLoad to avoid using in onShow
  onLoad () {
    this.eventId = bus.on('Event name', (a, b, c, d) => {
      // Support multiple parameters
      console.log(a, b, c, d)

      this.setData({
        a,
        b,
        c
      }
      // Call the page request function to refresh the data
      this.refreshPageData()
    })

    // Add an event listener that needs to be executed only once

    this.eventIdOnce = bus.once('Event name', () = > {// do some thing})}If the event id is not passed, the whole event listener will be removed. If the event id is passed, the event listener will be removedPage event monitor, avoid waste of extra resources, add event monitor/// After listening, it is recommended to remove the page when onUnload

  onUnload () {
    bus.remove('Event name'.this.eventId)
  }

  // Send an event that triggers the event listener to update the view
  // Support multiple parameter passing
  onClick () {
    bus.emit('Event name', a, b, c)
  }

Copy the code

For more detailed usage and examples, see the Github Iny -bus applets code

Implementation of iny-bus

  1. Writing in typescript and publishing to NPM, we need to set up a development environment, choose editing and packaging tools, and write publishing scripts. The details are not covered here, but the tools and libraries used are listed below
  • The basic packaging tool, which uses the excellent open source typescript-library-starter library, won’t go into details

  • The test tool used Facebook’s Jest

  • Build ci using [Travis -ci](www.travis-ci.org/)

  • Test coverage uploads using Codecov

  • Other details can be found in the source code package.json

  1. The specific implementation
  • First of all, let’s design our event center, and iny-Bus as the event center, we need a container to store our events, and we don’t want users to be able to access our container directly, so we need to privatize, like this



  class EventBus {

    private events: any[] = []}Copy the code
  • Then, what capabilities do our event center want to have, such as event listening on, which requires emit, which requires remove, which requires search, and we also need one-time events, such as once, which looks something like this

  interface EventBus {

    // Listen, we need to know an event name, we also need to send out the execution function, and we return a
    // the id is given to the user so that the user can remove the event listener
    on(name: string, execute: Function) :string

    // The only difference between once and on is that it is removed after the first execution
    // There is no difference between creation and on
    once(name: string, execute: Function) :string

    // remove, we need to remove the event listener, so we need the event name, in order to monitor multiple pages
    // Listen to the same event, so we can not remove the event listener all at once
    // Then we use the id from which the event was created. In the meantime, we return our event center, which can be chained
    remove(name: string, eventId? :string): EventBus

    Emit We need to tell the system the name of the event we want to send and the parameters it carries, and return the event instance
    emit(name: string. args:any[]): EventBus

    The find function returns an associative type, either the event or null
    find(name: string): Event | null

  }

Copy the code
  • Now that we’ve roughly designed our event center, we need to define the abilities and attributes of each of our events

  // Every thing, has a name, easy to remember and find, our events
  // We also need a name, and each of our events can be monitored n times, so we need a name
  // Each event has a container that holds the executor of each event

  interface Event {

    / / name
    name: string

    // Executor container
    executes: Execute[]
  }

  // We also need to determine the type of each executor. In order to accurately find the executor, we need an ID, which is also used
  // Delete the id of the executor, where eventType is used to indicate whether the executor is one-time, and execute is used for each executor
  // execute the function
  interface Execute {
    id: string
    eventType: EventType
    execute: Function
  }

Copy the code
  • Above, we mentioned eventType, which is used to indicate whether or not you are a one-time executor, and nothing in typescript is better suited for this situation than enumerations

// Declare the type of event executor

type EventType = 1 | 2


enum EventTypeEnum {
  // Common events
  NORMAL_EVENT = 1.// A one-time event
  ONCE_EVENT = 2
}

Copy the code
  • The basic type is defined, let’s write the implementation of the code, the first step, to implement the on once method

  
  class EventBus {

    /** * The container for storing events */
    private events: Event[] = []

    /** * on new event listener * @param name event name * @param execute callback function * @returns {string} eventId eventId. A user can cancel the event listener */


    on(name: string, execute: Function) :string {
      
      // Since there is no difference between on and once, we use addEvent, but to distinguish between on and once, we pass EventType
      return this.addEvent(name, EventTypeEnum.NORMAL_EVENT, execute)
    }

    * @param name event name * @param execute callback function * @returns {string} eventId eventId. A user can cancel the event listener */

    once(name: string, execute: Function) :string {
      / / in the same way on
      return this.addEvent(name, EventTypeEnum.ONCE_EVENT, execute)
    }

  }


Copy the code
  • Implement addEvent methods

  class EventBus {

    @param name @param execute */

    private addEvent(name: string, eventType: EventType, execute: Function) :string {
      const eventId = createUid()

      const events = this.events

      const event = this.find(name)

      if(event ! = =null) {
        event.executes.push({ id: eventId, eventType, execute })

        return eventId
      }

      events.push({
        name,
        executes: [
          {
            id: eventId,
            eventType,
            execute
          }
        ]
      })

      return eventId
    }

  }

Copy the code
  • Implement the find method

  class EventBus {
    /** * find the event * @param name */

    find(name: string): Event | null {
      const events = this.events

      for (let i = 0; i < events.length; i++) {
        if (name === events[i].name) {
          return events[i]
        }
      }

      return null}}Copy the code
  • Implement the remove method

  class EventBus {
    /** * remove Remove event listener * @param name Event name * @param eventId To remove a single event listener, pass the * @returns {EventBus} EventBus instance */

    remove(name: string, eventId: string): EventBus {
      const events = this.events

      for (let i = 0; i < events.length; i++) {
        if (events[i].name === name) {
          // Remove the specific operation function
          if (eventId && events[i].executes.length > 0) {
            const eventIndex = events[i].executes.findIndex(item= > item.id === eventId)

            if(eventIndex ! = =- 1) {
              events[i].executes.splice(eventIndex, 1)}}else {
            events.splice(i, 1)}return this}}return this}}Copy the code
  • Implement emit method

  class EventBus {
    /** * emit emit event * @param name Event name * @param args Other parameters * @returns {EventBus} EventBus instance */

    emit(name: string. args:any[]): EventBus {
      const events = this.events

      for (let i = 0; i < events.length; i++) {
        if (name === events[i].name) {
          const funcs = events[i].executes

          funcs.forEach((item, i) = >{ item.execute(... args)if (item.eventType === EventTypeEnum.ONCE_EVENT) {
              funcs.splice(i, 1)}})return this}}return this}}Copy the code
  • As an event center, to avoid creating multiple instances for consumers to use incorrectly, we can use the factory pattern to create a global instance for consumers to use, and provide consumers with a method to create new instances

  // Instead of creating a new EventBus directly, create an instance using a factory function, referring to the Axios source code
  function createInstance () :EventBusInstance {

    const bus = new EventBus()

    return bus as EventBusInstance
  }

  const bus = createInstance()

  // Extend the create method for users to create new bus instances
  bus.create = function create () {
    return createInstance()
  }
Copy the code

conclusion

There is only so much core code in Iny – Bus. In general, it is very small, but it can solve a lot of communication and data refresh problems we encounter in small programs. It is the best choice for page communication when using small programs of various platforms for native development. This ensures the stability and security of iny-Bus in use. Of course, each library is gradually improved from simple to complex. If you find any bugs or points that can be optimized in your use or source code, please feel free to mention PR or contact me directly

Finally, if iny-Bus has helped you or helped you in any way, please give the author a thumbs up and thank you for your thumbs up