preface

Released a subscription model is the observer pattern, so this article will first write a basic case, the observer pattern again on this foundation. Write a publish-subscribe pattern case, and then combined with the characteristics of both analysis of some common practice cases in the front-end development process, to help everybody to learn to understand the basic concepts and the difference between the two.

Note that both have something in common: a one-to-many mentality


Subscriber observer

  • subscriberAlso known asTopic Events, is essentially an object that is used for savingObserver (subscriber)The carrier, at the same timeAdd observers and notify all observersFor exampleThe company anIs aThe theme eventstheThe theme eventsInclude information about the company’s members, where they are, what they need to do, etc

  • The observerAlso known asThe subscriber, usually an object (can also be a function)

For convenience, this article refers to both as subscribers and observers


Observer model

We design an observer pattern: create a subscriber clickDep identified by Click, and then create two watch subscriptions to Click. The subscriber clickDep issues notifications, and the Watch fires collectively

// Construct the subscriber class
class Dep{
    constructor(id){
        this.id = id; // Subscriber identity
        this.subs = []
        
    }

    push(watch){
        watch.bind_id = this.id; // The subscriber to which the observer belongs
        this.subs.push(watch)
    }

    notify(){
        this.subs.forEach((watch) = >{
            watch.update()
        })
    }
}

// Construct the observer class
class Watch{
    constructor(update){
        this.update = update
    }
}

// Create a subscriber instance click dep
var clickDep = new Dep('click');

// Create an observer instance
var w1 = new Watch(() = >console.log('click watch 1'))
var w2 = new Watch(() = >console.log('click watch 2'))

// Add an observer
clickDep.push(w1)
clickDep.push(w2)

// Publish the message
clickDep.notify()
Copy the code


Publish and subscribe model

Create a scheduling center (event center) called Common to manage subscribers and observers, as well as add observer methods and publish subscriber notifications

class Common{
    constructor(id){
        this.id = id
        this.deps = {} // Used to save subscribers
    }

    createDep(key){
        this.deps[key] = []
    }

    createWatch(update){
        return {
            bind_id:null,
            update
        }
    }

    push(key,watch){
        watch.bind_id = key
        this.deps[key].push(watch)
    }

    notify(key){
        this.deps[key].forEach((watch) = >{
            watch.update()
        })
    }
}

// Create a scheduling center
const common = new Common('my common')

// Create a subscriber
common.createDep('click')

// Create an observer
var w1 = common.createWatch(() = >console.log('click watch 1'))
var w2 = common.createWatch(() = >console.log('click watch 2'))

// Add an observer
common.push('click',w1)
common.push('click',w2)

// Issue a notification
common.notify('click')
Copy the code


The difference between the two models

From the above example, we can see that the subscriber of the observer mode is independent of each other. In plain English, we create two subscriber instances, clickDep and touchDep, which are located in separate locations and are not managed by a unified object

The publishave-subscribe pattern solves this problem by creating a common object that manages subscribers and the observers they map to, as well as adding observers and publishing subscriber notifications


Application Case Analysis


  • DOM2 event binding: Bind the document with addEventListener to 3 event handlers. Click the page and trigger these three functions collectively

    document.addEventListener('click'.() = >{console.log('click handler 1')})
    
    document.addEventListener('click'.() = >{console.log('click handler 2')})
    
    document.addEventListener('click'.() = >{console.log('click handler 3')})
    Copy the code

    Case Study:

    • subscriber: js internal aboutClick event handler collector
    • The observerThree:Event handler
    • news: click the trigger


  • Promise: Make a simple Promise

    class Promise2{
    
      constructor(fn) {
    
        this.thenDep = []; // thenDep subscriber
        this.catchDep = [];
    
        fn(
          this.resolve.bind(this),
          this.reject.bind(this))}then(cb) { //thenDep adds subscriber CB
        this.thenDep.push(cb);
        return this; // return itself to make the chain call
      }
    
      catch(cb) {
        this.catchDep.push(cb);
        return this;
      }
    
      resolve(res) { // thenDep releases a message
        this.thenDep.forEach((cb) = > {
          cb(res)
        })
      }
    
      reject(res) {
        this.catchDep.forEach((cb) = > {
          cb(res)
        })
      }
    }
    
    
    new Promise2(function (resolve, reject) {
    
      console.log('handler')
    
      setTimeout(function () {
        resolve({ data: 'resolve message'})},2000)
    
    })
      .then((res) = > {
        console.log('then 1', res)
      })
      .then((res) = > {
        console.log('then 2', res)
      })
      .catch((error) = > {
        console.log('catch 1', error)
      })
      .catch((error) = > {
        console.log('catch 2', error)
      })
    Copy the code

    Case Study:

    • Subscriber: the [then/ Catch]Dep maintained internally by Promise2

    • Observer: Uses callbacks passed in by then and catch

    • Publish messages: Resolve and Reject are triggered


  • Vue template update Subscription: Declare username in vue component data, render with username in three places (e.g. text, HTML, attr), when

    
    <template>
        <dl>
            <dt>{{username}}</dt> 
            <dd v-html="username" :data-username="username"></dd>
            <dd>
                <button @click="username = 'Jeck'">change username</button>
            </dd>
        </dl>
    </template>
    
    <script>
    export default {
        data() {
            return {
            username:'Tom' // Vue creates a subscriber based on this attribute: [username]Dep}; },... }</script>
    Copy the code

    Case Study:

    • Subscriber: Creates [username]Dep objects inside vue

    • Observer: Three dom renders corresponding to the Update function [text/ HTML /attr]Update

    • Publish message: Username is changed


Understand Vue through design patterns

  • Subscriber creation stage: VUE source code, by binding each attribute of data (Object.defineProperty), at the same time create the subscriber [key]Dep of each attribute, through the set function of attribute binding, trigger the subscriber ([key]Dep) notification

  • The observer binding phase occurs when the template template is initially rendered. Vue binds the template update function to the attribute’s subscriber ([key] DEP) as an observer.

  • {{username}}

    {{username}}

  • [username]Dep will publish the message, and the observer textUpdate will be triggered.


Template update function: a function wrapped based on the native JS DOM API, such as passing arguments to node nodes and value targets, performs the following operations

  • Attribute update el.setAttribute
  • Text updates el.textContent
  • HTML update el. InnerHTML
  • Input to update the input value
  • The class update el. ClassList [add/remove]
  • Update el style. Style [name]


The content is summarized according to personal learning experience and notes. There may be some differences in understanding of knowledge points. I hope you can give me more advice and suggestions. Thank you ~ for your second post, please give me a thumbs up if you get anything, thank you ~