【vue3源码】响应式系统设计2(分支切换问题)

引入

上节已经把响应式基本架子已经搭建出来,但还是有一些比较隐式的问题存在

  1. 分支切换问题
  2. effect嵌套问题
  3. 递归调用

这节我们主要针对分支切换的问题来进行解决

问题引入
  • 上一节的整体代码
/**
 * 双向绑定
 * 响应式系统的简单实现
 */
let obj = {
  name: "zs",
  age: 15,
};
let activeEffectFn = null;
const bucket = new WeakMap();
function track(target, key) {
  // 每个对象下面对应的属性都有一个副作用函数
  let deps = bucket.get(target);
  if (!deps) {
    bucket.set(target, (deps = new Map()));
  }
  let effectFns = deps.get(key);
  if (!effectFns) {
    deps.set(key, (effectFns = new Set()));
  }
  effectFns.add(activeEffectFn);
}
function trigger(target, key) {
  let deps = bucket.get(target);
  if (!deps) return;
  let effectFns = deps.get(key);
  effectFns.forEach((effectFn) => {
    effectFn();
  });
}
let handle = {
  get(target, key, receiver) {
    // 在get中进行track收集依赖函数
    track(target, key);
    return target[key];
  },
  set(target, key, newValue, receiver) {
    target[key] = newValue;
    trigger(target, key);
    return true;
  },
};
function effect(fn) {
  // 执行effect的fn 会导致执行proxy的get
  activeEffectFn = fn;
  fn();
}
effect(() => {
  console.log("effect");
  if (obj.ok) {
    console.log(obj.name);
  }
});
obj.ok = false;
// 改变name的时候也会触发也会打印effect
obj.name = "ls";
  1. 问题点引出 分支切换
  2. 当ok为true的时候 副作用函数中应该收集的依赖是ok和name
  3. 当ok为false的时候副作用函数收集的依赖应该只是ok
  4. 所以问题来了无论ok为true还是false 我们都会收集name对应的副作用函数,当ok为false改变name的时候也会触发effect执行,这样就导致多余收集的问题
  5. 解决思路就是当我们每次trigger的时候effect中会触发get 就是属性会重新进行依赖收集 这时候我们就可以先清除原来收集过得副作用函数 当执行effect的时候在重新收集 关键函数为cleanup
解决思路
  • 当我们每次trigger的时候

     1 先执行effect 
     2 cleanup 清除副作用函数
     3 执行传入effect中的函数触发get 然后触发track
     4 重新收集依赖
    
关键函数
// 清除分支
function cleanup(activeFn) {
  for (let i = 0; i < activeFn.deps.length; i++) {
    let deps = activeFn.deps[i]; //这个是桶里面的set 每个属性下set里面包含的是它对应的副作用函数
    deps.delete(activeFn);
  }
  console.log(JSON.parse(JSON.stringify(bucket)));
  activeFn.deps.length = 0;
}

function effect(effectFn) {
  // 执行effect的fn 会导致执行proxy的get
  function activeFn() {
    cleanup(activeFn);
    activeEffectFn = activeFn;
    effectFn();
  }
  activeFn.deps = [];
  activeFn();
}

function track(target, key) {
  // 每个对象下面对应的属性都有一个副作用函数
  // ...xxx
  activeEffectFn.deps.push(effectFns);
}

整体代码

/**
 * 双向绑定
 * 响应式系统的简单实现
 */
let obj = {
  name: "zs",
  age: 15,
};
let activeEffectFn = null;
const bucket = new WeakMap();
function track(target, key) {
  // 每个对象下面对应的属性都有一个副作用函数
  let deps = bucket.get(target);
  if (!deps) {
    bucket.set(target, (deps = new Map()));
  }
  let effectFns = deps.get(key);
  if (!effectFns) {
    deps.set(key, (effectFns = new Set()));
  }
  effectFns.add(activeEffectFn);
}
function trigger(target, key) {
  let deps = bucket.get(target);
  if (!deps) return;
  let effectFns = deps.get(key);
  effectFns.forEach((effectFn) => {
    effectFn();
  });
}
let handle = {
  get(target, key, receiver) {
    // 在get中进行track收集依赖函数
    track(target, key);
    return target[key];
  },
  set(target, key, newValue, receiver) {
    target[key] = newValue;
    trigger(target, key);
    return true;
  },
};
function effect(fn) {
  // 执行effect的fn 会导致执行proxy的get
  activeEffectFn = fn;
  fn();
}
let proxyObj = new Proxy(obj, handle);

// 问题点引出 分支切换
// 当ok为true的时候 副作用函数中应该收集的依赖是ok和name
// 当ok为false的时候副作用函数收集的依赖应该只是ok
// 所以问题来了无论ok为true还是false 当我改变那么的时候都会触发副作用函数这是错误的

// 这是因为第一次ok为true的时候副作用函数收集的是ok和name 这两个属性下面都有副作用函数 所以当ok变成false的时候会触发副作用函数
// 解决思路就是当我们每次trigger的时候effect中会触发get 就是属性会重新进行依赖收集 这时候我们就可以进行清除一下属性的分支 把我们没有访问到的属性从桶里面给清除出去 关键函数为cleanup
effect(() => {
  console.log("effect");
  if (obj.ok) {
    console.log(obj.name);
  }
});
obj.ok = false;
obj.name = "ls";
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值