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,
1. Target Subject, owning method: Add / delete / notify Observer;
2. Observer object Observer, owning method: receive Subject Notification and handling of status change;
3. Target object Subject Notify all when the status changes Observer.
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.
Recommended reading:
The most detailed axios in history, you will understand it all after reading it
Technical difficulties and solutions encountered in vue background project