1. Vue3 dependency collection

  • effect: only ineffectIn the functionResponsive attributeWill be dependent on the collection
  • track:trackFunction will makeThe current property collects effects
  • trigger: Finds the property corresponding toeffectAnd perform

2. effectSide effect function

Although the VUE package is the integration of all modules, there is no effect method in VUE. That is, the effect method is present in reactivity, but it is not exposed to vUE, so we can only get the effect function through reactive reactivity

2.1 effectThe problem with the function

Problem 1: Problems with stack execution

const state =reactive({name:'zf'.age:12.address:'Huilongguan'})
effect(() = >{ // effect1      
    console.log(state.name); Effect1 / / collection
    effect(() = >{ // effect2 
        console.log(state.age); Effect2 / / collection
    });
    console.log(state.address); // Collect effect2 Expect to collect effect1
})
Copy the code

Problem 2: Preventing duplicate collections

effect(() = >{
    state.age++
})
Copy the code

2.2 implementationeffectfunction

export function effect(fn, options: any = {}) {
    // Create a reactive effect
    const effect = createReactiveEffect(fn, options);
    // Effect is executed once by default
    if(! options.lazy) { effect(); }return effect
}

let uid = 0;
let activeEffect;   // Store the current effect
const effectStack = []; / / the problem 1
function createReactiveEffect(fn,options){
    const effect = function reactiveEffect(){
        if(! effectStack.includes(effect)){// Problem 2 ensures that effect is not added to the effectStack
            try{
                effectStack.push(effect);
                activeEffect = effect;
                return fn(); // When the function is executed, the get method is executed
            }finally{
                effectStack.pop()
                activeEffect = effectStack[effectStack.length -1];
            }
        }
    }
    effect.id = uid++;        // Create an effect flag to distinguish the effect
    effect._isEffect = true;  // Identifies this reactive effect
    effect.raw = fn;          // leave the original function corresponding to effect
    effect.options = options; // Save user properties on effect

    return effect;
}
Copy the code

3. trackDepend on the collection

3.1 operators.ts

The main role of operators.ts is to provide two enumerated types, TrackOpTypes and TriggerOpTypes, for use by other modules

// reactivity/src/operators.ts
export const enum TrackOptypes {
    GET
}
export const enum TriggerOrTypes {
    ADD,
    SET
}
Copy the code

3.2 trackFunction implementation

Track allows an attribute in an object to collect its current effect function

// reactivity/src/effect.ts
const targetMap = new WeakMap(a);// track lets an attribute in an object collect its current effect function
export function track(target,type,key){ // Get the current effect
    // activeEffect: effect that is currently running
    if(activeEffect === undefined) {// This property is not dependent on the collection because it is not used in effect
        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));
    }
    if(!dep.has(activeEffect)){
        dep.add(activeEffect);
    }
}
Copy the code

Collect dependencies in the getter

// reactivity/src/baseHandler.ts
function createGetter(isReadonly = false, shallow = false) {
    return function get(target, key, receiver) {
      	// ...
        if(! isReadonly) {// When the effect function is executed, the value operation is performed so that the property remembers the corresponding effect functiontrack(target, TrackOpTypes.GET, key); }}}Copy the code

4. triggerTriggered update

Trigger finds the effect corresponding to the property and makes it execute (array, object)

export function trigger(target,type,key? ,newValue? ,oldValue?){
    
    // If effects are not collected for this attribute, no action is required
    const depsMap = targetMap.get(target);
    if(! depsMap)return;

    // All effects need to be stored in a new collection and executed together
    const effects = new Set(a);// The effect is changed
    const add = (effectToAdd) = >{
        if(effectToAdd){
            effectToAdd.forEach(effect= > effects.add(effect))
        }
    }

    // 1. Check whether the array length is changed, because the effect of changing the array length is large
    if(key === 'length' && isArray(target)){
        // If the corresponding length has a dependent collection, then update
        depsMap.forEach((dep,key) = > {
            if(key === 'length' || key > newValue){ // If the length of the change is smaller than the collected index, then this index also needs to trigger effect re-execution
                add(dep)
            }
        });
    }else{
        // Can be an object
        if(key ! = =undefined) {// This is a modification, not an addition
            add(depsMap.get(key));  // If it is a new one
        }
        // What if I change an index in the array
        switch(type){   // If an index is added, the length is updated
            case TriggerOrTypes.ADD:
                if(isArray(target) && isIntegerKey(key)){
                    add(depsMap.get('length'));
                }
        }

    }
    effects.forEach((effect:any) = >{
        if(effect.options.scheduler){
            effect.options.scheduler(effect);
        }else{
            effect()
        }
    })
}

// {name:'lucky', age:22} name => [effect,effect]
// weakMap key => {name:'lucky', age:22} value (map) => {name => Set, age => Set}
Copy the code