Introduce a,
Looking at the ref source code before, I found that it relies on track for collection and updates notification through trigger. Both functions are defined in the effect.ts file.
Second, source code analysis
2.1: track
// packages\reactivity\src\effect.ts
// Currently active side effects, similar to Watcher in VUe2
let activeEffect: ReactiveEffect | undefined
// Used to store the original object and activeEffect dependencies
// Is a 2-dimensional map
// Map of the first layer: key is the original object, value is a map
// Map: key is the attribute name of the original object, and value is the dependency set of the attribute
const targetMap = new WeakMap<any, KeyToDepMap>()
// The first argument: the original object
// The second argument: type
// The third argument: the attribute name of the original object, namely key
export function track(target: object.type: TrackOpTypes, key: unknown) {
// Determine whether track should be performed
if(! shouldTrack || activeEffect ===undefined) {
return
}
// There are no dependencies in the current targetMap to store the original object
let depsMap = targetMap.get(target)
if(! depsMap) {// If there is no storage, the original object is used as the key, and the value is a newly created empty map
targetMap.set(target, (depsMap = new Map()))}// There is no dependency set for key
let dep = depsMap.get(key)
if(! dep) {// If there is no dependency set, create an empty set to store the dependency
depsMap.set(key, (dep = new Set()))}// Determine if there are currently activeeffects in the dependency set that should be collected
if(! dep.has(activeEffect)) {Activeeffects should be collected if they are not already collected
dep.add(activeEffect)
ActiveEffect dependencies are also collected synchronously, which means that both sides collect each other
activeEffect.deps.push(dep)
}
}
Copy the code
Conclusion: The track method mainly maintains targetMap, which maintains the ActiveEffects that should be collected by the attributes of the original object.
2.2 the trigger
export function trigger(
target: object.type: TriggerOpTypes, key? : unknown, newValue? : unknown, oldValue? : unknown, oldTarget? :Map<unknown, unknown> | Set<unknown>
) {
const depsMap = targetMap.get(target)
// This property has never been tracked and does not need to trigger the update notification
if(! depsMap) {// never been tracked
return
}
// Define a set to store the activeEffects to be notified
const effects = new Set<ReactiveEffect>()
// Define the add method
const add = (effectsToAdd: Set<ReactiveEffect> | undefined) = > {
if (effectsToAdd) {
effectsToAdd.forEach(effect= > {
if(effect ! == activeEffect || effect.allowRecurse) { effects.add(effect) } }) } }// A change of type clear notifies all dependencies
if (type === TriggerOpTypes.CLEAR) {
// collection being cleared
// trigger all effects for target
depsMap.forEach(add)
} else if (key === 'length' && isArray(target)) {
// A change in the array length type notifies the dependency corresponding to the key that exceeds the new length
depsMap.forEach((dep, key) = > {
if (key === 'length' || key >= (newValue as number)) {
add(dep)
}
})
} else {
// schedule runs for SET | ADD | DELETE
if(key ! = =void 0) {
add(depsMap.get(key))
}
}
// Define the run method to perform the effect update
const run = (effect: ReactiveEffect) = > {
if(effect.options. Scheduler executes) {// Execute via scheduler
effect.options.scheduler(effect)
} else {
// Execute directly
effect()
}
}
// Execute the run method on all dependencies that should be notified
effects.forEach(run)
}
Copy the code
Summary: The trigger method determines which key dependencies should be updated based on type and performs the update of the dependencies.