1. What is the observer model?

The observer pattern defines a one-to-many dependency that allows multiple observer objects to listen to a target object at the same time and notify all observer objects when the state of the target object changes so that they can automatically update it.Copy the code

2. Differences between observer and publish-subscribe

  • The observer mode is the most frequently used js design mode, and the probability of being asked in interviews is the highest. The Observer mode ‘alias’ publish-subscribe mode cannot be strictly equated between the two.
  • Publish-subscribe: publishers do not directly touch subscribers, but a unified third party does the actual communication operation;
  • The difference between the two mainly lies in whether there is a third party and whether publishers can directly perceive subscribers.
  • The observer solves the coupling problem between modules and can realize the data communication between two independent modules, but it does not completely solve the coupling problem. The observed must maintain a set of observers.
  • In the publish-subscribe model, publishers do not need to be aware of subscribers at all, and events are registered and triggered on a third-party platform (event bus) independent of both parties, achieving complete decoupling.
  • The usage scenarios of the two are different, and each has its own application scenarios:
  • If the relationship between the two modules is stable and necessary, then the observer pattern is sufficient. We tend to use publish-subscribe when there is a lot of independence between modules and there is no need to create dependencies between the two purely for data communication.

3. Realization of VUE two-way data binding (responsive system)

3.1 Application of observer mode in VUE
  • In VUE, each instance of a component has a corresponding Watcher instance object, which records properties as dependencies during component rendering and then informs Watcher to recalculate when setters for dependencies are called, causing the associated component to be updated — a typical observer pattern.
  • Flow chart of responsive system in VUE official website

3.2 Implement a simple VUE responsive code
In the implementation logic of vUE two-way data binding, there are three key roles:
  • Observer: In the role structure of vUE two-way data binding, the so-called Observer is not only a listener, but also needs to forward the monitored data. It is also a publisher.
  • Wathcer (Subscriber) : The Observer forwards the data to the actual subscriber, the Watcher object. When Watcher receives new data, it updates the view;
  • Compile (compiler) : responsible for the scanning and parsing of each node element instruction, the initialization of instruction data, the creation of subscribers;
Core code:
Function observe(target){// Target is an object, If (target && typeof Target === 'object'){object.keys (target).foreach ((key)=>{// defineReactive assigns a 'listener' to the target attribute DefineReactive (target, key, target[key])})} Val){// Attribute values can also be of type object, Observe (val) // Install listener object.defineProperty (target,key,{// Enumerable :true, // No additional information can be configured. false, get:function(){ return val }, Set :function(newVal){val = newVal dep.notify()}} constructor(){this.subs = []} AddSub (sub){this.subs.push(sub)} notify(){this.subs.foreach ((sub)=>{sub.update()})}}Copy the code

4, Implement an Event Bus/Event Emitter

Event Bus/Event Emitter, as a global Event Bus, functions as a communication bridge and an Event center.Copy the code
Implement an Event Bus
This. Handlers = {}} // the on method is used to install event listeners. It takes the target event name and callback function as arguments on(evnetName, cb){// Check if the target event name has a listener queue if(! Handlers [eventName]){// If not, Handlers [evnetName] = []} this. Handlers [eventName].push(cb)} // The emit method is used to fire the target event. It takes the eventName and listener function input arguments emit(eventName,... Handlers [eventName] {this. Handlers [eventName]; Handlers = this.handlers[eventName].slice() const handlers = this.handlers[eventName]. Handlers. ForEach ((callback)=>{callback(... Args)})}} // Remove the specified callback function from the event callback queue off(eventName, cb){ const callbacks = this.handlers[eventName] const index = callbacks.indexOf(cb) if(index ! == -1){callbacks.splice(index,1)}} // Register the word listener function once(eventName,cb){// Wrap the callback so that it is automatically removed from the const wrapper = (... args)=>{ cb(... args) this.off(eventName, wrapper) } this.on(eventNme,wrapper) } }Copy the code