Vue3响应式个人理解(二)分支切换和ClearUp

文章参考了霍春阳的《Vue.js设计与实现》,是自己在阅读过程中的一些思考和理解

分支切换的定义:
随着条件的不同,代买走不同的路径,类似于三元表达式和if语句。
目前的问题:
分支切换会产生遗留的副作用函数。例子:

  const data = {
    ok: true,
    text: 'mean'
  }

这样的数据,根据上篇文章的响应式代码,会生成各自的副作用函数,被各自的依赖集合收集。

data
	- ok
		- effectFn
	- text
		- effectFn

但是当我将ok的值改为false,并触发副作用函数执行后,text的值不会被读取,所以理论上,text的副作用函数就不应该存在了。然而,按照之前的代码,text的副作用函数仍然存在于text的依赖集合中,也就是产生了副作用函数的遗留,从而导致了不必要的更新。这里的不必要更新指的是,如果我们在此时修改text的值,仍然会执行其对应的副作用函数,但是页面根本不会展示text,这样的函数执行是没有意义的。
解决办法:在每次的副作用函数执行时,可以先把它从所有它存在的依赖集合中删除,因为副作用函数执行后,会建立新的联系。代码如下

 function effect(fn) {
    const effectfn = () => {
      //执行时,将其改名为activeEffect
      activeEffect = effectfn
      fn()
    }
    //用于存储与该副作用函数相关的依赖集合
    effectfn.deps = []
    //执行
    effectfn()
  }

收集过程(track函数):

 get(target, proxy) {
      if(!activeEffect) return
      //从桶中获取当前对象的Map,因为bocket中可以存储对个对象
      let depsMap = bocket.get(target)
      //如果没有则立即创建一个,并放在bocket中
      //这里映射为 target--->Map<key,Set()>
      if(!depsMap) {
        bocket.set(target, (depsMap = new Map()))
      }
      //从对象的众多key中拿到我们要修改的key的Set()集合
      //如果没有则立即创建,并加入到depsMap中
      //这里的映射为key--->Set(fn,fn,fn....)
      let deps = depsMap.get(proxy)
      if(!deps) {
        depsMap.set(proxy, (deps = new Set()))
      }
      //将我们传入的修改函数加入到对应key的Set()集合中
      deps.add(activeEffect)
  ⭐️   // 此时deps就是存储当前副作用函数的集合,将其存放在此副作用函数的集合中
      activeEffect.deps.push(deps)
      //返回读取的值
      return target[proxy]
    },

当我们在执行track函数时,就会将当前的副作用函数添加到依赖集合deps中,这说明deps就是一个与当前副作用函数存在联系的集合,那就把它存在activeEffect的deps中,从而完成收集。
这样,我们就可以在每次执行副作用函数时,根据effectfn.deps获取的所有相关联的依赖集合,进而将副作用函数从依赖集合中删除。代码如下:

  function effect(fn) {
    const effectfn = () => {
      clearup(effectfn)
      activeEffect = effectfn
      fn()
    }
    effectfn.deps = []
    effectfn()
  }
  
function clearup(effectfn) {
  for (let i=0; i<effectfn.length; i++) {
    const deps = effectfn.deps[i]
    deps.delete(effectfn)
  }
  effectfn.deps.length = 0
}

这样虽然可以达到清除副作用函数的作用,但是会导致无限循环。原因是在trigger函数中,我们会执行当前key的副作用函数集合中的副作用函数,执行时,会调用clearup函数清空当前副作用函数的deps集合,并且会再次执行副作用函数:

 const effectfn = () => {
      clearup(effectfn)    //<----这个会执行,然后清空
      activeEffect = effectfn
      fn()   // <---这个会执行,然后会读取值,进而调用get操作,然后有添加新的副作用函数进入到其副作用函数集合中,然后又会继续执行,进而无限循环。
    }

避免手段中trigger函数中添加新的集合代替原effect集合进行遍历:

 set(target, proxy, value, reciver) {
      //原对象中修改要变的值
      target[proxy] = value
      //获取到target的Map,没有则返回
      const depsMap = bocket.get(target)
      if(!depsMap) return
      //拿到要修改的key的Set集合()
      const effects = depsMap.get(proxy)
      //依次执行集合中的函数
      const effectsToRun = new Set(effects)  //<---新的集合
      effectsToRun.forEach(effect => effect())  //<--代替遍历
      // effects && effects.forEach(fn => fn())
      return true
    }

这里的简单实现,会将删除的effectfn在执行trigger的时候再次放入到其Key的Set集合中,而源码中是会有一个相等判断。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值