当前位置: 首页 > Web前端 > vue.js

Vue.js设计与实现学习总结(第4章4)无限递归

时间:2023-04-01 11:09:01 vue.js

constdata={foo:1}constobj=newProxy(data,{/*...*/})effect(()=>obj.foo=obj.foo+1)这个操作会导致栈溢出:UncaughtRangeError:Maximumcallsacksizeexceeded这个操作会先读取obj.foo的值,触发track操作,将sideeffectfunction压入栈中。这个时候添加一个赋值,这个时候就会触发触发操作。从堆栈中弹出副作用函数并执行它。这种情况下,副作用函数还在执行,下一次执行又开始了,导致无限递归调用自身,出现栈溢出错误。在这个操作中,读取和设置同一个副??作用函数activeEffect,所以在触发trigger的时候加上一个条件:如果trigger触发的副作用函数和当前正在执行的副作用函数相同,则执行不会被触发:functiontrigger(target,key){constdepsMap=bucket.get(target)if(!depsMap)returnconsteffects=depsMap.get(key)consteffectsToRun=newSet()effects&&effects.forEach(effectFn=>{if(effectFn!==activeEffect){effectsToRun.add(effectFn)}})effectsToRun.forEach(effectFn=>effectFn())}到目前为止响应完整的代码//用于存储副作用函数的桶constbucket=newWeakMap()//用于存储已注册副作用的函数letactiveEffect=undefined//副作用FunctionstackconsteffectStack=[]functioncleanup(effectFn){for(letitmeofeffectFn.deps){itme.delete(effectFn)}effectFn.deps.length=[]}functioneffect(fn){consteffectFn=()=>{cleanup(effectFn)//当调用当前副作用函数时,将其赋值给全局变量activeEffect=effectFn//在调用副作用函数之前将函数压入栈effectStack.push(effectFn)fn()//当前副作用函数执行后,将被弹出栈effectSrack。pop()//activeEffect恢复到之前的值activeEffect=effectStack[effectStack.length-1]}effectFn.desp=[]effectFn()}constdata={text:'helloworld',ok:true}constobj=newProxy(data,{//拦截读取操作get(target,key){track(target,key)//返回属性值returntarget[key]},//拦截设置操作set(target,key,newVal){//设置属性值target[key]=newValtrigger(target,key)}})functiontrack(target,key){//没有activeEffect,直接返回if(!activeEffect)returntarget[key]//根据目标从'bucket'返回depsMap,也是一个Map类型:key--->effectsletdepsMap=bucket.get(target)//如果depsMap不存在,新建一个Map关联targetif(!depsMap)bucket.set(target,(depsMap=newMap()))//根据key从depsMap中获取deps,是一个Set类型//存放当前key相关的所有副作用函数:effectsletdeps=depsMap.get(key)//如果deps不存在,同样新建一个Set,并关联key0effectfunctiontothe'bucket'deps.add(activeEffect)}functiontrigger(target,key){//根据target从'bucket'获取depsMap,它是key-->effectsconstdepsMap=bucket.get(target)if(!depsMap)return//根据keyeffects获取所有副作用函数consteffects=depsMap.get(key)//执行副作用函数consteffectsToRun=newSet()effects&&effects.forEach(effectFn=>{//如果副作用函数触发执行的和当前执行的副作用函数不同如果执行的副作用函数相同,则不会触发执行())}effect(()=>{console.log('effectrun');document.body.innerText=obj.ok?obj.text:'not'})setTimeout(()=>{obj.ok=false},2000)