Standing on the shoulders of giants to see VUE, vUE design and implementation from Huo Chunyang. The author explains the underlying vuE3 implementation step by step in the form of questions. The idea is very clever, here will be the main logic in the book series, which is also my own record after reading, I hope that through this form can communicate with everyone to learn.
Chapter 5 non – primitive value response scheme
5.1 understand Proxy and Reflect
A Proxy can create an object and implement the Proxy for other objects. A Proxy can only Proxy objects, but cannot Proxy non-object values, such as strings and Booleans.
Reflect is a global object that has the same methods as objects, but accepts a third parameter, receiver. You can think of it as this during a function call.
const obj = new Proxy(data, {
get(target, key) {
track(target, key)
return target[key]
},
set(target, key, newVal) {
target[key] = newVal
trigger(target, key)
}
})
Copy the code
The problem with Proxy is that the “this” in get might change the dependency collection so that no response can be made, so we can use reflect to replace the previous method
const obj = new Proxy(data, {
get(target, key, receiver) {
track(target, key)
return Reflect.get(target, key, receiver)
},
set(target, key, newVal) {
target[key] = newVal
trigger(target, key)
}
})
Copy the code
5.2 Working principle of object and Proxy
The actual semantics of an object are specified by the object’s internal methods. Internal methods are the methods that are called inside the engine when we operate on an object. Function objects have an internal method [[Call]] deployed inside them, whereas normal objects do not.
The interceptor functions specified when creating a proxy object actually use the internal methods and behaviors derived from defining the proxy object itself, rather than specifying the internal methods and behaviors of the prosted-object.
5.3. How to proxy Object
Question 1: How to intercept key in OBj
The result of the IN operator is obtained by calling an abstract method called HasProperty. The corresponding interceptor function is named HAS, so we need to define the HAS method when intercepting
const obj = new Proxy(data, {
get(target, key, receiver) {
track(target, key)
return Reflect.get(target, key, receiver)
},
set(target, key, newVal) {
target[key] = newVal
trigger(target, key)
},
has(target, key) {
track(target, key)
return Reflect.has(target, key)
}
})
Copy the code
Question 2: For… In circulation
In the same way for… In corresponds to the interception object is ownKeys, so we need to define the ownKeys method when interception. And define a Symbol function that stores the side effects collection. When the trigger function is executed, the side effects function associated with ITERATE_KEY is pulled out and executed.
const ITERATE_KEY = Symbol(a)ownKeys(target) {
track(target, ITERATE_KEY)
return Reflect.ownKeys(target)
}
/ / the trigger function
const iterateEffects = depsMap.get(ITERATE_KEY)
iterateEffects && iterateEffects.forEach(effectFn= > {
if(effectFn ! == activeEffect) { effectsToRun.add(effectFn) } })Copy the code
Problem 3: Delete the property delete obj.foo
Delete Corresponding property deleteProperty. So we need to define the deleteProperty method when we intercept, and we don’t need to perform side effects when we delete.
deleteProperty(target, key) {
const hadKey = Object.hasOwnProperty.call(target, key)
const res = Reflect.deleteProperty(target, key)
if (res && hadKey) {
trigger(target, key, 'DELETE')}return res
}
Copy the code
5.4 Reasonably low trigger response
Problem 4: When the changed value does not change and does not trigger the response, add the comparison and deal with the NaN case.
set(target, key, newVal, receiver) {
const oldVal = target[key]
const type = Object.hasOwnProperty.call(target, key) ? 'SET' : 'ADD'
const res = Reflect.set(target, key, newVal, receiver)
if(oldVal ! == newVal && (oldVal === oldVal || newVal === newVal)) { trigger(target, key, type) }return res
},
Copy the code
Problem 5: Inherited properties on the stereotype
const obj = {}
const proto = { bar: 1 }
const child = reactive(obj)
const parent = reactive(proto)
Object.setPrototypeOf(child, parent)
effect(() = > {
console.log(child.bar)
})
child.bar = 2
/ / 1
/ / 2
/ / 2
Copy the code
The side effect was performed twice.
When reading the value of the child.bar property, the object obj of the Child proxy has no bar property of its own, so it gets the prototype of the object obj and the value of parent-bar. In the same way, when setting a property, we also look for the value on the prototype. In this case, both child and parent have established a response, so the side effect is executed twice.
The update will only be triggered if the receiver is the target’s bring object, thus masking the update caused by the prototype. Proxy objects can read raw data through the RAW property
child.raw === obj // true
parent.raw === proto // true
Determine in set whether receiver is a proxy object for target
if (target === receiver.raw) {
if(oldVal ! == newVal && (oldVal === oldVal || newVal === newVal)) { trigger(target, key, type) } }Copy the code
5.5 shallow response and deep response
When an attribute value is read, it is first checked to see if the value is an object. If it is an object, the reactive function is recursively called to wrap the value into reactive data and return it. This is the first deep response. Control by setting an isShallow variable.
function creatReactive(obj, isShallow = false) {
return new Proxy(obj, {
get(target, key, receiver) {
if (key === 'raw') {
return target
}
const res = Reflect.get(target, key, receiver)
if (isShallow) {
return res
}
track(target, key)
if (typeof res === 'object'&& res ! = =null) {
return reactive(res)
}
return res
},
set(target, key, newVal, receiver) {
// }})}Copy the code
5.6. Read-only and shallow read-only
The createReactive function takes an isReadonly variable and determines if it is read-only on set. And when the first object is a read-only property, there is no need to call track in GET to track the response.
get() {
if(! isReadonly) { track(target, key) } },set() {
if (isReadonly) {
console.warn(` properties${key}Is read-only information ')
return true}}Copy the code
5.7. Proxy arrays
5.7.1 Array index and length