Beep in advance
In the previous section, we talked about the principle of responsiveness, which is relatively general and difficult to understand, so we started writing this section directly. At the end of the chapter, I converted the observer object into a js version for you to test directly. You can copy and debug while understanding the next writing process
start
The detection of observer is an object or array, so we need to pass this parameter (named value) in at the beginning
class ObserverNext { $value: any; constructor(value) { ... }
In the previous response principle, we said that we should deeply recursively analyze the data in this object (that is, the data option), so we also have to have a walk method
class ObserverNext { $value: any; constructor(value) { ... this.walk(value) } private walk(obj: Object | Array<any>) { for (const [key, val] of Object.entries(obj)) { if (typeof val == "object") { //Judge arrays and objects at the same time new ObserverNext(key,val, obj); } } }
Now we have completed deep recursion, but there is no analysis process, so we have to write another method called detect to analyze. Here, we use Proxy to monitor (analyze) the object
//detect private method in observer private detect(val: any, parent: any) { const dep = this.dep//Defined in constructor const proxy = new Proxy(val, { get(obj, property) { if (!obj.hasOwnProperty(property)) { return; } dep.depend(property); return obj[property]; }, set(obj, property, value) { obj[property] = value; dep.notify(property); return true; }, }); }
When the object get s the data, we collect the dependency. When the object set s the data, we notify the dependency update,
Moreover, the proxy should be available externally. Our solution here is to replace it directly on the parent object
parent[key] = proxy;
So we have to pass in the parent and key from the beginning.
So our constructor becomes
constructor(key,value, parent) { this.$key=key; this.$value = value; this.$parent = parent; this.dep = new Dep(); this.walk(value); this.detect(value, parent); }
Now our observer has become
class ObserverNext { $value: any; $parent: any; $key:string dep: any; constructor(key,value, parent) { this.$key=key; this.$value = value; this.$parent = parent; this.dep = new Dep(); //def(value, "__ob__", this); this.walk(value); this.detect(value, parent); } private walk(obj: Object | Array<any>) { for (const [key, val] of Object.entries(obj)) { if (typeof val == "object") { //Judge arrays and objects at the same time new ObserverNext(key,val, obj); } } } private detect(val: any, parent: any) { const dep = this.dep const key=this.$key const proxy = new Proxy(val, { get(obj, property) { if (!obj.hasOwnProperty(property)) { return; } dep.depend(property); return obj[property]; }, set(obj, property, value) { obj[property] = value; dep.notify(property); //if(parent.__ob__)parent.__ob__.dep.notify(key) return true; }, }); parent[key] = proxy; } }
But now there is a bug. If the son object is updated, it will only notify its dependent collector (dep) to update, and the parent object will not perceive any exceptions!
So we have to notify the Observer class instance of the parent object to update
Therefore, we should record the observer instance in each object
It is roughly as follows:
constructor(key,value,parent){ ... def(value, "__ob__", this); //Equivalent to value__ ob__= this; ... }
Then, in the set of proxy, we need to notify the parent object to update
set(){ if(parent.__ob__)parent.__ob__.dep.notify(key) }
Final results
Our original version is typescript version, but in order to facilitate you to test directly in the console, I converted it to js version for you to debug
class Dep { constructor() {} depend() { console.log("Dependency collection"); } notify() { console.log("Dependency update"); } } function def(obj, key, val, enumerable=false) { Object.defineProperty(obj, key, { value: val, enumerable: !!enumerable, writable: true, configurable: true, }); } class ObserverNext { constructor(key, value, parent) { this.$key = key; this.$value = value; this.$parent = parent; this.dep = new Dep(); def(value, "__ob__", this); this.walk(value); this.detect(value, parent); } walk(obj) { for (const [key, val] of Object.entries(obj)) { if (typeof val == "object") { //Judge arrays and objects at the same time new ObserverNext(key, val, obj); } } } detect(val, parent) { const dep = this.dep; const key = this.$key; const proxy = new Proxy(val, { get(obj, property) { if (!obj.hasOwnProperty(property)) { return; } dep.depend(property); return obj[property]; }, set(obj, property, value) { obj[property] = value; dep.notify(property); if (parent.__ob__) parent.__ob__.dep.notify(key); return true; }, }); parent[key] = proxy; } } const vm = { data: { attr1: { a: 1, b: 2, c: 3, }, array: [1, 2, 3], }, }; new ObserverNext('data',vm.data,vm); //test //vm. data. Attr1 - > return dependency collection
file
#Touch hands to teach you to realize a simple vue (1) responsive principle
#Touch hands to teach you to implement a simple vue (2) start writing observer
#Touch hands to teach you to implement a simple vue (3) start writing dep and watcher