vue3 effect 实现思路

vue3 effect 实现思路

首先了解这张图
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

const state = reactive({ name: 'cld', age: 26, arr: [1, 2, 3], obj: { attr1: 'attr1', attr2: 'attr2' } });
effect(()=>{
  console.log('effect内执行:',state.obj.attr1)
})
state.obj.attr1='cld2'

在这里插入图片描述
大概思路清楚了 开始分析代码
创建effect函数
传入一个函数与options对象

export function effect (fn, options = {}) {
  const effect = createReactiveEffect(fn, options);
  if (!options.lazy) { 
    effect();
  }
  return effect;// 非立即执行的情况
}

createReactiveEffect内部处理

let uid = 0; // 给每个effect加唯一标识
let activeEffect // 当前正在执行的effect;
const effectStack = []; //判断当我目前effect正在执行的时候 不要继续添加 不要再执行导致死循环

function createReactiveEffect (fn, options) {
  const effect = function reactiveEffect (params) {
    if (!effectStack.includes(effect)) { // 防止死循环
      try { // fn可能会出错 要退出
        effectStack.push(effect);
        activeEffect = effect;
        fn(); // 执行传进来的函数
      } finally {
        effectStack.pop();
        activeEffect = undefined;
      }
    }
  };
  effect.options = options;
  effect.id = uid++; // 唯一标识
  effect.deps = []; // 存储会导致它执行的数据
  return effect;
}
  • 当我执行effect的时候 会执行上面的函数
  • 在effect内部获取数据的时候 会执行new proxy 中的 get函数
  • 主要是 new proxy 里面的 get 函数的拦截处理触发track函数
  • 去收集该属性相关的effect在数组中
  function get (target, key, receiver) { // proxy+reflect
    const res = Reflect.get(target, key, receiver);// == target[key];
    // 主要是这里的触发函数 
    track(target, 'get', key);
    if (isObject(res)) { // 如果绑定的是对象 继续递归 知道是基本数据为止
      return reactive(res);
    }
    return res;
  };

track函数的目的是组成weakmap 对象,把依赖的effect都存起来

// weakmap结构
// {a:1,b:2}:{
//   a:[effect],
//   b:[effect1,effect2]
//}
const targetMap = new WeakMap(); // 用法与map一致,弱引用 不会内存泄漏
export function track (target, type, key) { //假如 target={a:1,b:2} key=a
  if (activeEffect === undefined) {
    return; // 说明取值的时候没有effect正在执行 该次取值不在effect中,不依赖于effect
  }
  let depsMap = targetMap.get(target);// 根据target在WeakMap中取值 
  if (!depsMap) { // 没有相关依赖说明是第一次绑定 新建依赖map
    depsMap = new Map();//{}:{}
    targetMap.set(target, depsMap);// targetMap = { {a:1,b:2}:{} }
  }
  let depsSet = depsMap.get(key); //根据key在map中取值  depsMap里面找key(a)的依赖数组
  if (!depsSet) {
    depsSet = new Set(); //Set{}
    depsMap.set(key, depsSet);// {a:1,b:2}: {a:Set[]}
  }
  // 执行到某个effect里面获取值的时候,存入当前这个effect,
  // 当这个值在其他地方更新的时候 会重新执行这个effect
  if (!depsSet.has(activeEffect)) {
    // 记录,这个属性改变会触发哪些effect
    depsSet.add(activeEffect); // {a:1,b:2}:{key:Set[...oldEffect,effect]}
    // 双向记录, effect记录哪些effect需要触发它
    activeEffect.deps.push(depsSet); //effect.deps=[Set[...oldEffect,effect]]
  }
}
  • 当我在effect外部去更改值的时候
  • 会触发new Proxy中的 set 拦截 从而触发trigger函数
  • trigger函数查找到改属性对应的weakMap上的 effect 依赖数组
function set (target, key, value, receiver) {
    const hasKey = hasOwn(target, key);
    const oldValue = target[key]; // Reflect.set 操作前还是历史数据
    const res = Reflect.set(target, key, value, receiver); // 更改值并且 返回结果为true / false;
     // 新增属性
    if (!hasKey) {
	  console.log('用户修改值', target, key);
     // 触发trigger
      trigger(target, 'add', key, value);
    } else if (value !== oldValue) {
      //修改操作
      console.log('用户修改值', target, key);
      // 触发trigger
      trigger(target, 'set', key, value);
    }
    return res;
  };

trigger函数的目的是在weakmap 对象里面找到该属性的值,也就是依赖的effect数组

export function trigger (target, type, key, value, oldValue) {
// {
//  {a:1,b:2}:{
//    a:[effect],
//    b:[effect1,effect2]
//   }
// {c:3,d:4}:{
//    c:[effect],
//    d:[effect1,effect2]
//   }
//}
  const depsMap = targetMap.get(target);  //假如 target={a:1,b:2} key=a
//  {a:1,b:2}:{
//    a:[effect],
//    b:[effect1,effect2]
//   }
  console.log('target trigger',type,key,target);
  if (!depsMap) {
    return;
  }
  if (key !== null) {
    const depsSet = depsMap.get(key);// 得到 a:[effect]
    if (depsSet) {// [effect]
      runEffect(depsSet); // 遍历执行
    }
  }


}
const runEffect = (effects) => {
  effects.forEach(effect => {
    effect();
  });
};

trigger 要考虑特殊情况
比如数组添加元素等 后续更新

  if (type === 'add') {
    const isArr = Array.isArray(target);
    const depsSet = depsMap.get(isArr ? 'length' : "");
    if (depsSet) {
      runEffect(depsSet);
    }
  }

vue3 关于effect的源码部分github

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Embrace924

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值