What is the publish-subscribe model?

The subscription publishing pattern defines a one-to-many dependency for multiple subscriber objects to listen to a topic object simultaneously. This topic object notificates all subscriber objects when their state changes, enabling them to automatically update their state.

The addEventListener API, for example, is a publish-subscribe model

For those of you who have used Vue, you can compare it to Watch

Let’s look at an example

var observe={
    fnsObj: {},// Subscription method
    on:function(key,fn){
        if(! observe.fnsObj[key]){ observe.fnsObj[key] = [] } observe.fnsObj[key].push(fn) },/ / release
    emmit:function(key,value){
        if(observe.fnsObj[key].length){
            var fnsList = observe.fnsObj[key]
            for(var i=0; i<fnsList.length; i++){ fnsList[i].call(this,value)
            }
        }
        
    },
    // Delete the subscriber
    remove:function(key){
        for(var k in observe.fnsObj){
            if(k===key) delete observe.fnsObj[key]
        }
    }
    
}
Copy the code

Ok, so let’s try to call it

// We subscribe to two listeners
observe.on('say'.function(e){
console.log('i can hear he say: '+e)
})
observe.on('say'.function(e){
console.log('he say: '+e)
})
Copy the code

Follow up with the news

// Publish the message
observe.emmit("say"."Hey, this is the publisher")
Copy the code

You can see that the console returns two messages, which are the printouts from the subscriber we just defined

I can hear he say: I can hear him say: I can hear him say: I can hear him sayCopy the code

This is the publish subscribe model, and I’m sure many of you know the concept, but how to actually use it in a project is a big problem.

After all, design patterns don’t feel very common, and requirements can be implemented without design patterns, so let me highlight how the publish-subscribe pattern is used for one requirement I encountered in VUE.

The practical application

1. Requirement introduction

My project is the internal human resource management system of the company. Therefore, menus need to be obtained according to different permissions. Some drop-down box data and some basic information need to be obtained immediately after logging in. Data should be obtained after calling the interface and stored in VUEX

Currently, these methods are written in app.vue


// Get the basic data
  this.loadMenu()
  this.loadBasicData()
  this.loadUserInfo()

Copy the code

So I have to think about two things

  1. You can only pull this data once you’re logged in
  2. The current page is refreshed. If it is logged in, you need to pull data again. Otherwise, no operation is performed

1. Regular solutions

This is a fairly common requirement, and similar ones are common. The usual solution is to do this:

  1. Put these methods inside a mixin
  2. In login.vue, after successful login, save a state to sessionStorage, and call these methods to pull data
  3. App. Vue inside the created life cycle to determine whether the state of sessionStorage is logged in, such as logged in to pull data

Ok, the requirement is resolved, but the problem is, what if these methods can only be placed in app.vue, such as this project, I did not write app.vue. Is it another front end that doesn’t want to put these methods in mixins?

It would be fine if we could listen for changes to sessionStorage. But neither Watch nor computed can listen for changes in sessionStorage.

So at this point we can try a publish-subscribe model

1, create oneobserve.js

class Observe {
  constructor() {
    this.fnsObj = {}
  }
     // Subscription method
  on(key, fn) {
    if (!this.fnsObj[key]) { this.fnsObj[key] = [] }
    this.fnsObj[key].push(fn)
  }
      / / release
  emmit(key, value) {
    if (this.fnsObj[key].length) {
      var fns = this.fnsObj[key]
      for (let i = 0; i < fns.length; i++) {
        fns[i].call(this, value)
      }
    }
  }
      // Delete the subscriber
  remove(key) {
    for (var k in this.fnsObj) {
      if (k === key) {
        delete (this.fnsObj[k])
      }
    }
  }
}
const observeSession = new Observe()

export default observeSession
Copy the code

2. Introduce him in app.vue, defining both listener and publisher

import observeSession from './utils/Observe'. created(){// If the status is logged in after the refresh, obtain the data
     sessionStorage.getItem('login') = = ='login' && this.loadSelectVal()
   // Define a global method. When window.setsessionStorage is called, the publisher publishes the information
      window.setSessionStorage = (key, value) = > {
        observeSession.emmit('watchSesStore', { key, value })
      }
  // Listen to the sessionStorage login status change, if the state is logged in to obtain data, the listener listening information
      observeSession.on('watchSesStore', e => {
        sessionStorage.setItem(e.key, e.value)
       e.value === 'login'&&this.loadSelectVal()
      })
}
Copy the code

Finally, we added a line to him when he logged in successfully on login.vue

 window.setSessionStorage('login'.'login')
Copy the code

And the exit is handled in router-js routing control

// If you jump to the login page, the login status is logged out
 if (to.name === "Login") {
    next()
    window.setSessionStorage && window.setSessionStorage('login'.'logout')}else{...Copy the code

So far the demand is perfectly solved.

conclusion

Personally, I think that when you open the door of design mode, you will find a lot of ideas to solve the problem, and you will write more high-quality code. This article is just a brick to introduce jade, just a small demand encountered in the project, I hope to give you some inspiration, if there are shortcomings, welcome to make improvements, thank you.