Observer mode and publish subscriber mode in vue

Observer mode

The target object and the observer object are interdependent. The observer observes the state of an object. If the state of the object changes, it will notify all observers who depend on the object,

The target object {Subject has the following methods: Add / delete / notify} Observer;

The Observer object , Observer, has the following methods: receive and process the , Subject , status change notification;

When the target object {Subject} state changes, all} observers are notified.

In Vue, the responsive data change is the observer mode. Each responsive attribute has a DEP, which stores the watcher that depends on this attribute. The watcher is a function of observing the data change. If the data changes, DEP will notify all observers to call the update method. Therefore, the observer needs to be collected by the target object in order to inform all observers who depend on it. Then why does the watcher store dep? This is because the currently executing watcher needs to know which dep notified him at this time.

After beforeCreate, created called observe(data) before initializing response data, the following is simplified version of code (no handling of array hijacking).

class Observer {
    // The attribute description of value needs to be redefined
    constructor(value) {
      this.walk(value); // The data is monitored during initialization
    }
    walk(data) {
      Object.keys(data).forEach((key) => {
        defineReactive(data, key, data[key]);
      });
    }
  }
  
  function defineReactive(data, key, value) {
    // value may be an object that needs to be hijacked recursively, so the data cannot be nested too deeply
    observe(value);
    let dep = new Dep();
    Object.defineProperty(data, key, {
      get() {
        // If there is a watcher, let the watcher remember dep to prevent duplicate DEP, and DEP also collects this watcher
        if (Dep.target) {
          dep.depend();
        }
        return value;
      },
      set(newVal) {
        // If the data is unchanged, it will not be processed
        if (value === newVal) return;
        observe(newVal); // If the new value is an object, it is intercepted recursively
        value = newVal; // Set new value
        dep.notify(); // Notify the collected watcher to update
      },
    });
}
function observe(data) {
    // If it is not an object, it will not be processed. isObject is a function used to determine whether it is an object
    if (Object.prototype.toString.call(data)!== '[object Object]') return;
    // Data observation is realized through classes, which is convenient to expand and generate examples
    return new Observer(data);
}
observe(data)

After created, the mountComponent mount component is called before mouted, and the following is simplified version of the code (watcher that does not handle watch and computed).

class Dep {
    static target = null
    constructor() {
      this.id = id++;
      this.subs = []; // Store dependent watcher s
    }
    depend() {
      // Let the executing watcher record dep, and dep will also record the watcher
      Dep.target.addDep(this);
    }
    addSub(watcher) {
      // Add observer object
      this.subs.push(watcher);
    }
    notify() {
      // Trigger update method of observer object
      this.subs.forEach((watcher) => watcher.update());
    }
}
class Watcher {
    constructor(vm, exprOrFn) {
      this.vm = vm;
      this.deps = [];
      // It is used for de duplication to prevent multiple same DEPs from being stored when the same data is fetched multiple times
      this.depId = new Set();
      // exprOrFn is updateComponent
      this.getter = exprOrFn;
      // Update page
      this.get();
    }
    get() {
      Dep.target = watcher; // Collect the watcher before taking the value
      this.getter.call(this.vm); // Call updateComponent to update the page
      Dep.target = null; // Delete the watcher after the value is taken
    }
    // Called when dep.depend ent executes
    addDep(dep) {
        let id = dep.id;
        let has = this.depId.has(id);
        if (!has) {
            this.depId.add(id);
            // Watchdog storage dep
            this.deps.push(dep);
            // dep storage watcher
            dep.addSub(this);
        }
    }  
    // Update the page method, which is called when dep.notify is executed
    update() {
        this.get(); // Render updates as soon as the data is modified
    }
}
function mountComponent(vm) {
    // Render update page
    let updateComponent = () => {
      let vnode = vm._render(); // Generate virtual node vnode
      vm._update(vnode); // Convert vnode to real node
    };
    // Each component calls a render watcher
    new Watcher(vm, updateComponent);
}
mountComponent(vm)

 

Publish subscribe mode

Based on an event center, the object receiving the notification is the subscriber. You need to subscribe to an event first. The object triggering the event is the publisher. The publisher notifies each subscriber by triggering the event. Event binding in js is the publish subscribe mode

Compared with the observer mode, the publish subscribe mode has more event centers, and the subscriber and publisher are not directly related.

The event bus in vue is the publish subscribe mode used

 

// Event bus
class Bus {
  constructor() {
    // An array used to record events and listen for them
    this.listeners = {};
  }
  // Adds a listener for the specified event
  $on(eventName, handler) {
    this.listeners[eventName].add(handler);
  }
  // Cancel listening event
  $off(eventName, handler) {
    this.listeners[eventName].delete(handler);
  }
  // Trigger event
  $emit(eventName, ...args) {
    this.listeners[eventName].forEach((fn) => fn(...args));
  }
}

See this article for the specific use of event bus Event bus of vue

The difference between observer mode and publish subscribe mode

The target and the observer are interdependent.

The publish subscribe mode is called by a unified scheduling center, and the publisher and subscriber do not know the existence of each other.

Keywords: Front-end Vue.js

Added by sanfly on Mon, 03 Jan 2022 04:01:15 +0200