preface
As a React fan and heavy reader, vue3. X upgrade points have to be changed a little bit. It’s not an important one, but at least it’s one of them. The Proxy!!!!!!
The Proxy object
- Set and deleteProperty are required to return Boolean values. In strict mode, a Type Error occurs if false is returned
Reflect is reflect. it is used to get or set a running member during code runtime. In the past, some methods were randomly mounted to objects, such as: Object.getproperty (),Reflect also has a method reflect.getProperty (), which does the same thing. If there is a method in Reflect, it is recommended to use the method in Reflect
'use strict'
const target={
foo:'xxxx'.bar:'yyyy'
}
const proxy = new Proxy(target,{
get(target,key,receiver){
return Reflect.get(... arguments) },set(target,key,receiver){
return Reflect.get(... arguments) },deleteProperty(target,key){
return Reflect.get(... arguments) }, }) proxy.foo='zzs'
Copy the code
- Reaciver used in Proxy and Reflect. In Proxy, reciver is a Proxy object or an object that inherits from Proxy. Reciver in Reflect: If a getter is set in the target object, this in the getter points to Reciver
const obj = {
get foo() {console.log(this)
return this.bar
}
}
const proxy =new Proxy(obg,{
get(target,key,reciver){
if(key == 'bar') {return 'value-bar'
}
return Reflect.get(target,key)
}
})
console.log(proxy.foo)
Copy the code
Reactive function
- Receives a parameter and determines whether the parameter is an object
Reactive only converts objects into reactive objects, which is different from ref
- Create the interceptor object handler and set get/set/deleteProperty
- Return Proxy object
const isObject = val= >val ! = =null && typeof val === 'object'
const convert = target= > isObject(tartget) ? reactive(target) : target
/** * determine if an Object itself has a specified property */
const hasOwnProperty= Object.prototype.hasOwnProperty
const hasOwn = (target,key) = >hasOwnProperty.call(target,key)
export function reactive(tartget) {
// Tartget returns directly if it is not an object
if(! isObject(tartget))return tartget
// Otherwise convert target to a proxy object
// Trap
const handler = {
get(target, key, receiver) {
// Collect dependencies
console.log('get'.'key')
/** * return the value of the corresponding key in the target. * If the value of the key is an object, you need to recursively collect the dependencies of the next level in the getter. You also need to convert all the properties in the getter to reactive */
const result = Reflect.get(target, key, receiver)
return convert(result)
},
set(target, key, value, receiver) {
const oldValue = Reflect.get(target, key, receiver)
let result = true
if(oldValue ! == value) { result =Reflect.set(target, key, value, receiver)
console.log('set',key,value)
}
return result
},
deleteProperty(target, key) {
/** * First determine if the current target has its own key attribute * if it has a key attribute, trigger an update if the key was successfully deleted from the target */
const hadkey = hasOwn(target,key)
const result = Reflect.deleteProperty(target, key)
if(hadkey&& result){
// Trigger the update
console.log('delete',key)
}
return result
},
}
return new Proxy(tartget, handler)
}
Copy the code
Collect rely on
const product = reactive({
name: 'iPhone'.price: 5000.count: 3
})
let total = 0
/** * The first load executes the arrow function in effect. * The arrow function accesses the product, which is a reactive object. That is, when the proxy object * accesses product.price, it accesses the get method of product.price. In the get method, it collects dependencies. The process of collecting dependencies is to store this property and the callback function * property is associated with the object, So the get method of the proxy object will first store the target, then the target attribute price, and then store the corresponding arrow function. So there's a relationship here, the target object, the corresponding property, and the corresponding arrow function. * This attribute is used to find the corresponding function when the update is triggered */
effect(() = > {
total = product.price * product.count
})
console.log(total)
product.price = 4000
console.log(total)
product.count = 1
console.log(total)
Copy the code
Three collections are created during dependency collection:
- targetMap
New WeakMap(), targetMap is used to record the target object and a dictionary, that is, the attributes of the target object. The Map used is a weak reference, that is, the stored key is an object. Because it is a weak reference, when the target loses the reference, the target will be destroyed. The value of targetMap is depsMap
- depsMap
The new Map() is a dictionary, the key is the attribute name of the target object, and the value is a set that stores elements that do not repeat themselves
- dep
New Set(), which stores the effect function. Because you can call an effect multiple times and access the same property in an effect, the property will collect multiple dependencies for multiple effects
This structure allows you to store the target object, its properties, and the effect function. A property may correspond to more than one function. When an update is triggered in the future, the effect function can be found and executed based on the properties of the target object.
Implementation of effect function
let activeEffect = null
export function effect(callback) {
// activeEffect records the callback
activeEffect = callback
/** * After calling effect, perform a callback * to access the properties of the responsive object to collect dependencies */
callback()
// Set this parameter to null after collecting dependencies
activeEffect = null
}
Copy the code
Implementation of TRACK function
let targetMap = new WeakMap(a)export function track(target, key) {
/** * Collect the dependent track function by first finding depsMap using the targetMap attribute. * If not, create a depsMap for the current object and add it to the targetMap. * If a dep is found, find the corresponding deP in depsMap based on the current attribute. * dep stores the effect function. If not found, create a map for the current property and store it in depsMap. * If the collection corresponding to the current property is found, the current effect function is stored in the corresponding DEP. * /
if(! activeEffect)return
let depsMap = targetMap.get(target)
if(! depsMap) { targetMap.set(target, (depsMap =new Map()))}let dep = depsMap.get(key)
if(! dep) { depsMap.set(key, (dep =new Set()))
}
dep.add(activeEffect)
}
Copy the code
The trigger to realize
export function trigger (target, key) {
const depsMap = targetMap.get(target)
if(! depsMap)return
const dep = depsMap.get(key)
if (dep) {
// Iterate over all of the effec functions and execute them one by one
dep.forEach(effect= > {
effect()
})
}
}
Copy the code
ref
export function ref (raw) {
// Check whether raw is an object created by ref, and return if it is
if (isObject(raw) && raw.__v_isRef) {
return
}
let value = convert(raw)
const r = {
// Create the identity attribute. The identity is the object created by ref
__v_isRef: true,
get value () {
// Call track to collect dependencies
track(r, 'value')
return value
},
set value (newValue) {
// Determine whether the old and new values are equal
if(newValue ! == value) { raw = newValue value = convert(raw) trigger(r,'value')}}}return r
}
Copy the code
reactive Vs ref
- Ref can convert basic data types into reactive objects. The. Value attribute is used when fetching data. The value attribute can be omitted from templates. Reactive does not convert basic data types into reactive objects
- The object returned by ref, when re-assigned to value, is also responsive
- Reactive reassignment of the returned object loses the response because the reassigned object is no longer a proxy object
- Reactive objects are not allowed to be structured. If they want to be structured, they are toRefs into reactive objects
toRefs
Reactive object (reactive object) reactive object (reactive object) reactive object (reactive object
export function toRefs (proxy) {
const ret = proxy instanceof Array ? new Array(proxy.length) : {}
for (const key in proxy) {
ret[key] = toProxyRef(proxy, key)
}
return ret
}
// Convert each attribute to the object returned by ref
function toProxyRef (proxy, key) {
const r = {
__v_isRef: true,
get value () {
return proxy[key]
},
set value (newValue) {
proxy[key] = newValue
}
}
return r
}
Copy the code
computed
The computed function internally listens for changes in the responsive properties of the getter through effect. This is because when the getter is executed in effect, the accessed properties collect dependencies. When the data changes, the effect function is executed again and the getter results are stored in the result
export function computed (getter) {
const result = ref()
effect(() = > (result.value = getter()))
return result
}
Copy the code