Reactive = data hijacking + publish subscriber pattern

Data hijacking:

The vue2. X version uses object.defineProperty + to override the array method

Grammar:

Object.defineProperty(obj, prop, descriptor)

  • Obj Specifies the object for which attributes are to be defined
  • Prop Specifies the name of the property to define or modify
  • Descriptor Property descriptor to define or modify

Descriptor attributes:

  • Different: Determines whether the attribute of the descriptor can be changed and deleted.
  • Enumerable: Whether it is enumerable;
  • Value: the value of the attribute.
  • Writable: Whether the value of an attribute can be changed;
  • The key to data hijacking is the get/set function, which performs corresponding operations when attributes are read and assigned values.

Array: Set is triggered only when using assignment operations such as arr = []. Vue2. x listens by overriding array. prototype. Arr [1]= 2; arr[1]= 2; Change to arr. Splice (1,1,2) to be responsive; Set (student, ‘age’, 18); Vue. Set (student, ‘age’, 18); The principle of vue.set is to judge new content, and if the content is not reactive, a dependency collection (defineReactive, notify) is done again.

Vue3. X uses Proxy

  • If proxy is used to do data hijacking, the above problems about new attributes and array operations do not exist.
  • Proxy objects are used to create a Proxy for an object to intercept and customize basic operations (such as property lookup, assignment, enumeration, function calls, and so on).

Grammar:

const p = new Proxy(target, handler)
Copy the code
  • Target: To useProxyThe wrapped target object (which can be any type of object, including a native array, a function, or even another proxy).
  • Handler: An object that usually has functions as properties, and the functions in each property define the agents that perform the various operationspBehavior.

Specific usage reference:

Developer.mozilla.org/zh-CN/docs/…

Published subscriber model:

Simple code implementation:

// Add get set for (let key in data) {defineReactive(data, key); }} const defineReactive = function(obj, key) {const dep = new dep (); Let val = obj[key]; DefineProperty (obj, key, {// Enumerable: true, // The property can be modified without any additional information. true, get() { console.log('in get'); // Call addSub in the dependency collector to collect the dependencies between the current properties and the Watcher. Dep.depend (); return val; }, set(newVal) { if (newVal === val) { return; } val = newVal; // When the value changes, notify the dependent collector and update each Watcher that needs to be updated. dep.subs dep.notify(); }}); } const observe = function(data) { return new Observer(data); } const Vue = function(options) { const self = this; // Assign data to this._data, If (options && typeof options.data === 'function') {this._data = options.data. Apply (this); } this.mount = function() {new Watcher(self, self.render); } // this. Render = function() {with(self) {_data.text; }} // Listen to this._data observe(this._data); } const Watcher = function(vm, fn) { const self = this; this.vm = vm; Dep.target = this; This. AddDep = function(Dep) {dep.addSub(self); } vm._render this.update = function() {console.log('in watcher update'); fn(); } this.value = fn(); this.value = fn(); this.value = fn(); // Dep. Target = null; // Dep. Target = null; } const Dep = function() { const self = this; This. target = null; // Store the notification Watcher this.subs = []; This.depend = function() {if (dep.target) {self.addSub(dep.target); // I didn't write this because I wanted to restore the source code process. Dep.target.addDep(self); } // Add Watcher this.addsub = function(Watcher) {self.subs.push(Watcher); This.notify = function() {for (let I = 0; i < self.subs.length; i += 1) { self.subs[i].update(); } } } const vue = new Vue({ data() { return { text: 'hello world' }; } }) vue.mount(); // in get vue._data.text = '123'; // in watcher update /n in getCopy the code

Source: juejin. Cn/post / 684490…

  • The Observer is responsible for converting data to getter/setter form;
  • The Dep is responsible for managing the dependency list of data; It is a publish-subscribe model that connects upstream to the Observer and downstream to Watcher
  • Watcher is the de facto data dependency, responsible for forwarding changes to the data to the outside world (rendering, callback); First pass the data to an Observer and put it in getter/setter form; When the Watcher instance reads the data, it fires the getter, which is collected into the Dep repository. When data is updated, setters are triggered to notify all Watcher instances in the Dep repository of updates, and the Watcher instances are responsible for informing the outside world