1. Object monitoring method
1.1 Listener object in vue2: Object.defineProperty(obj,key,descriptor)
Object.defineProperty is used in vue2 to monitor objects:
//Define an object first const obj = { age: 16, height: 1.88, grade: 99 } //Loop through the object and monitor each property Object.keys(obj).forEach(key => { let value = obj[key] Object.defineProperty(obj, key, { // Operate by accessing attribute descriptors get() { console.log(`${key} property is accessed`); return value }, set(newValue) { console.log(`${key} attribute is assigned`); if (value === newValue) return value = newValue } }) }) obj.age obj.height = 2.0
But there are some disadvantages in this method: First, the original intention of Object.defineProperty is not to monitor all properties in an object; second, if we want to monitor richer operations, such as adding properties and deleting properties, then Object.defineProperty is powerless.
So how can we monitor objects correctly? In ES6, the proxy class is added. If we listen to the related operations of an object, then we can create a proxy object (proxy object) first, and then all operations on the object will be completed through the proxy object.
1.2 Listening object in vue3: const proxyObj = new Proxy(target, handler)
In vue3, a proxy object is created through the Proxy class, and subsequent operations are directly performed on the proxy object instead of the original object:
//Define an object first const obj = { name: 'why', age: 20, height:1.98 } //Create proxy object //parameter: //target (listening object obj), key (property to be set), value (value of new property), receiver (calling proxy object objProxy) const objProxy = new Proxy(obj, { get(target, key, receiver) { console.log(`The ${key} property of the object is accessed`, target); // return target[key] return Reflect. get(target, key, receiver) }, set(target, key, value, receiver) { if (target[key] !== value) { // target[key] = value Reflect.set(target,key,value,receiver) console.log(`The ${key} property of the object is set to a value`, target); } }, has(target, key) { // return key in target return Reflect.has(target,key) }, deleteProperty(target, key) { // delete target[key] Reflect.deleteProperty(target,key) } }) console.log(objProxy.name); console.log(objProxy.age); objProxy.name = 'kobe' objProxy.age = 20 console.log('name' in objProxy); delete objProxy.height console.log(objProxy);
The Reflect object is used in the above code. If you don’t know the Reflect object, click to understand Reflect
2. Implement responsive principles
2.1 Vue2 implements responsive
//1. Define a global function object to save dependent functions let activeReactiveFn = null //2. Define a listener function function watchFn(fn) { activeReactiveFn = fn fn() activeReactiveFn = null } //3. Define a class to add a corresponding dependency function to each property of the object, as well as an execution function class Depend { constructor() { this.reactiveFns = new Set() } //add dependent function addDepend() { if (activeReactiveFn) { this.reactiveFns.add(activeReactiveFn) } } //execute dependent function notify() { this.reactiveFns.forEach(fn => { fn() }) } } //4. Create a weakMap to store dependencies corresponding to multiple properties of multiple objects const targetMap = new WeakMap() function getDepend(target, key) { // The process of obtaining the map according to the target object let map = targetMap. get(target) if (!map) { map = new Map() targetMap.set(target, map) } // Get the depend object according to the key let depend = map. get(key) if (!depend) { depend = new Depend() map.set(key, depend) } return depends } //Note: The main difference between vue2 and vue3 listening objects // 5. Monitor the object and return the monitored object function reactive(obj) { Object.keys(obj).forEach(key => { const value = obj[key] Object.defineProperty(obj, key, { get() { const depend = getDepend(obj, key) depend. addDepend() return value }, set(newValue) { value = newValue const depend = getDepend(obj, key) depend. notify() } }) }) return obj } //6, define an object const obj = { name: "why", age: 18 } // 7. Call the function to monitor the object reactive(obj) //8. The function triggered when the properties of the collection object are monitored watchFn(() => { console.log(obj.name, 'monitoring object obj's name property'); }) watchFn(() => { console.log(obj.age, 'monitor the age attribute of obj object'); }) //9, operate on the object obj.name = 'code' obj.age = 22
2.2 Vue3 implements responsive
//1. Define a global function object to save dependent functions let activeReactiveFn = null //2. Define a listener function function watchFn(fn) { activeReactiveFn = fn fn() activeReactiveFn = null } //3. Define a class to add a corresponding dependency function to each property of the object, as well as an execution function class Depend { constructor() { this.reactiveFns = new Set() } //add dependent function addDepend() { if (activeReactiveFn) { this.reactiveFns.add(activeReactiveFn) } } //execute dependent function notify() { this.reactiveFns.forEach(fn => { fn() }) } } //4. Create a weakMap to store dependencies corresponding to multiple properties of multiple objects const targetMap = new WeakMap() function getDepend(target, key) { // The process of obtaining the map according to the target object let map = targetMap. get(target) if (!map) { map = new Map() targetMap.set(target, map) } // Get the depend object according to the key let depend = map. get(key) if (!depend) { depend = new Depend() map.set(key, depend) } return depends } //Note: The main difference between vue2 and vue3 listening objects // 5. Create proxy object function //parameter: //target (listening object obj), key (property to be set), value (value of new property), receiver (calling proxy object objProxy) function reactive(obj) { return new Proxy(obj, { get(target, key, receiver) { // Get the corresponding depend according to target.key const depend = getDepend(target, key) // Add a response function to the depend object depend. addDepend() return Reflect. get(target, key, receiver) }, set(target, key, value, receiver) { Reflect.set(target, key, value, receiver) const depend = getDepend(target, key) depend. notify() } }) } //6, define an object const obj = { name: "why", age: 18 } // 7. Create a proxy object through a function const objProxy = reactive(obj) //8. The function triggered when listening to the properties of the proxy object watchFn(() => { console.log(objProxy.name, 'monitor the name attribute of the proxy object objProxy'); }) watchFn(() => { console.log(objProxy.age, 'monitor the age attribute of the proxy object objProxy'); }) //9, operate on the proxy object objProxy.name = 'code' objProxy.age = 22
In the above code, the newly added data structures in ES6 such as Set(), WeakMap(), and Map() are used. Click on them to learn more details.
The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge