Vue.js---分支切换与cleanup

发布于:2025-05-13 ⋅ 阅读:(14) ⋅ 点赞:(0)

4.2 分支切换与cleanup

1、分支切换

01 const data = { ok: true, text: 'hello world' }
02 const obj = new Proxy(data, { /* ... */ })
03
04 effect(function effectFn() {
05   document.body.innerText = obj.ok ? obj.text : 'not'
06 })

什么是分支切换?就是

document.body.innerText = obj.ok ? obj.text : 'not'

但是这就会导致一个问题“可能会产生遗留的副作用函数”

eg:当obj.ok的值为false的时候,obj.text不会被读取,只会触发obj.ok的读取操作。所以effectFn不应该被obj.text所收集在这里插入图片描述

但是我们目前还是会遗留副作用函数,导致一些不必要的更新

如何解决???

3、清除函数

每次副作用函数执行时,我们可以先把它从所有与之关联的依赖集合中删除

在这里插入图片描述

当副作用函数执行完之后会重新建立连接,但在新的连接中是不会包含遗留的副作用函数的

大概思路:在 effect 内部我们定义了新的 effectFn 函数,并为其添加了 effectFn.deps 属性,该属性是一个数组,用来存储所有包含当前副作用函数的依赖集合。

设置deps属性:

 function effect (fn) {
    const effectFn = () => {
      // 当其执行时,将其设置为当前激活的副作用函数
      activeEffect = effectFn; 
      fn();
    }
    // deps用来存储所有与该副作用函数相关联的依赖集合
    effectFn.deps = [];
    // 执行副作用函数
    effectFn();
  }

建立依赖集合与effectFn的联系

  function track(target , key){
    if(!activeEffect){
        return target[key];
      }
      // 根据tartget取来的depsMap,它是一个map类型
      let depsMap = bucket.get(target);
      // 如果不存在
      if(!depsMap){
        // 创建一个
        bucket.set(target, (depsMap = new Map()));
      }
      // 根据key取来的deps,它是一个set类型
      let deps = depsMap.get(key);
      // 如果不存在
      if(!deps){
        // 创建一个
        depsMap.set(key, (deps = new Set()));
      }
      deps.add(activeEffect); // 添加当前活跃的副作用函数
    // 存储存在联系的依赖集合,建立起依赖集合和effectFn的关系
    deps.add(activeEffect);
    // deps就是一个与当前副作用函数存在联系的依赖集合
    // 把它存在activeEffect里面的deps中
    activeEffect.deps.push(deps);
  }

开始一个个删除联系

  function effect (fn) {
    const effectFn = () => {
      // 调用clearup函数完成清除工作
      clearUp(effectFn);
      // 当其执行时,将其设置为当前激活的副作用函数
      activeEffect = effectFn; 
      fn();
    }
    // deps用来存储所有与该副作用函数相关联的依赖集合
    effectFn.deps = [];
    // 执行副作用函数
    effectFn();
  }
   // 清除函数
   function clearUp (effectFn){
     // 遍历然后进行删除
     for(let i = 0 ; i < effectFn.deps.length ; i++){
       const deps = effectFn.deps[i];
       // 移除
       deps.delete(effectFn);
     }
     // 最后重置effectFn.deps数组
     eff.deps.length = 0;
     
   }

我们的清除遗留副作用函数就大功告成啦!但是为什么一直死循环!!?

问题在trigger函数当中

  // 触发变化
  function trigger(target , key , newVal){
     const depsMap = bucket.get(target);
      if(!depsMap){
        return;
      }
      const effects = depsMap.get(key);
      effects && effects.forEach(fn => fn()); // 只触发与键相关的副作用函数
  }

在这个函数中,我们遍历effects集合,当副作用函数执行的时候会调用clearUp函数清除,实际上就是从effects集合中将当前执行的副作用函数剔除,但是副作用函数的执行又会导致其重新被收集到集合中,陷入死循环。

如何解决?新构造一个集合并遍历它,代替直接遍历effects函数,就不会出现这样的问题啦

  // 触发变化
  function trigger(target, key, newVal) {
    const depsMap = bucket.get(target);
    if (!depsMap) {
      return;
    }
    const effects = depsMap.get(key);
    const effectsToRun = new Set(effects);
    effectsToRun.forEach(effectFn => effectFn());
    // effects && effects.forEach(fn => fn()); // 只触发与键相关的副作用函数
  }

造一个集合并遍历它,代替直接遍历effects函数,就不会出现这样的问题啦

  // 触发变化
  function trigger(target, key, newVal) {
    const depsMap = bucket.get(target);
    if (!depsMap) {
      return;
    }
    const effects = depsMap.get(key);
    const effectsToRun = new Set(effects);
    effectsToRun.forEach(effectFn => effectFn());
    // effects && effects.forEach(fn => fn()); // 只触发与键相关的副作用函数
  }

总结:解决分支问题,主要是clearUp函数在副作用函数执行前清除旧的依赖关系,确保只有当前实际使用的属性变化时才触发更新,值得注意的是我们遍历effects集合,当副作用函数执行的时候会调用clearUp函数清除,实际上就是从effects集合中将当前执行的副作用函数剔除,但是副作用函数的执行又会导致其重新被收集到集合中,陷入死循环,我们要构造一个集合并遍历他


网站公告

今日签到

点亮在社区的每一天
去签到