第六节: vue3响应式补充

153 阅读2分钟
const state = reactive({name: 'name'})

delete state.name // 调用 deleteProperty
'name' in state   // 调用 has
for(let key in state){} // 调用ownkeys
state.name = 'jdlyp'  // 调用set

多次触发更新处理

对proto和对象都进行代理的时候会触发多次更新,源码中对这种现象做了处理

处理方法

// 在set的时候
// don't trigger if target is something up in the prototype chain of original

if (target === toRaw(receiver)) { // 减少原型链的触发

    if (!hadKey) { // 添加

        trigger(target, TriggerOpTypes.ADD, key, value)

    } else if (hasChanged(value, oldValue)) { // 如果前后置有变化触发修改逻辑

        trigger(target, TriggerOpTypes.SET, key, value, oldValue)

    }

}
// 示例1 这种情况本来会触发两次 处理完只触发一次
let obj = {};

let proto = {a:1}

let proxyProto = new Proxy(proto, {

    get(target,key,receiver) {

        return Reflect.get(target,key,receiver)

    },

    set(target,key,value,receiver){

        // console.log(proxyProto , receiver == myProxy); // 这个地方要屏蔽的

        return Reflect.set(target,key,value,receiver)// 不要考虑原型链的set

    }

})

Object.setPrototypeOf(obj,proxyProto); // 给obj赋值会触发proxyProto的set

let myProxy = new Proxy(obj,{  // proxy(obj)

    get(target,key,receiver) {

        return Reflect.get(target,key,receiver)

    },

    set(target,key,value,receiver){

        console.log(receiver === myProxy)

        return Reflect.set(target,key,value,receiver); // 调用reflect.set 会触发原型链的set

    }

})

myProxy.a = 100; // 内部的特点 



console.log(myProxy.a,proto)

// 多次触发更新
// 示例2 这种情况也只触发一次
let people = reactive({eat:'吃',drink:'喝'}); // 防止

let person = reactive({eat:'吃'});



Object.setPrototypeOf(person,people)

effect(()=>{ //people收集一次  perso都会收集一次

    console.log(person.eat,'触发几次'); // 这里访问的是person.eat 无论处不处理都只触发一次

})

//  person上没有找到了父类people  不处理的话会触发两次
person.eat = '呵呵';

数组

1) 收集索引

修改长度要触发索引的触发 改的是length

const arr = reactive([1]);
effect(()=>{
    // 通过索引访问可以收集依赖
    console.log(arr[0]); // 这里没有收集length
    arr.push(1) //这里收集了length
})
// arr[0] = 2;  正常更新值的情况
arr.length = 0; //修改长度要触发索引的触发  改的是length
arr[100] = 2 // 触发length更新
arr.push(1)  // 触发length更新

处理方法

depsMap.forEach((dep, key) => { // 如果修改的是长度,要让大于长度的依赖放入

  if (key === 'length' || key >= (newValue as number)) {

    deps.push(dep)

  }

})

2) 长度收集

修改索引 触发长度更新;数组新增属性触发length更新

const arr = reactive([1]);
effect(()=>{
    // 通过索引访问可以收集依赖
    console.log(arr.length);
})
debugger
arr[100] = 2; // 修改索引,如果是添加应该触发长度更新

3) 数组方法 访问变异方法会访问数组的长度,会对长度进行依赖,这里我们停止收集调用方法产生的依赖

const arr = reactive([]);
effect(()=>{
    arr.push(1); // 会修改属性 同时 访问length
    console.log('重新push')
})  
debugger
arr.push(1); // 修改length

effect

runner.effect = _effect 可以通过runner.effect 访问effect 上的方法

1). effect中嵌套effect的runner

 // 数组依赖收集的方式 
 const { reactive, readonly, toRaw, markRaw, effect, ref } = VueReactivity;
 const state = {name:'lyp'}
 // 1). effect中嵌套effect的runner 不做处理的话会死循环

 debugger

 let runner = effect(()=>{ 
     state.name = 'super man';
 });
 debugger
 effect(runner); 

处理方法

 if ((fn as ReactiveEffectRunner).effect) { // 如果此函数是effect的返回的runner,则找到原函数

    fn = (fn as ReactiveEffectRunner).effect.fn

 }