《Vue设计与实现》第四章 第6节 计算属性

1.《Vue设计与实现》computed计算属性

let computed = (getter) => {
    let effectFn = effect(getter, {
        lazy: true, // 这个参数就表示,副作用函数(getter)先不执行,而是返回给effectFn变量
    })
    // 我们需要在返回一个对象,让这个对象在访问value属性时,才会计算对应的值
    let obj = {
      get value() {
        return effectFn(); // 当我们在访问value属性时,才会去执行这个函数
      }
    }
    return obj;
}

effect修改的地方,这里只是写了,lazy参数具体干什么用,没有把完整的effect实现写出来

let effect = (fn, options) => {
   // 我们将副作用函数包裹一层
   let effectFn = () => {
     activeEffect = effectFn;
     const res = fn();
     return res;
   }
   effectFn.deps = [];
   effectFn.options = options;
   if (options.lazy) {
      return effectFn; // 我们就先不执行这个副作用函数
   } else {
     effectFn(); // 否则我们就直接调用了这个函数
   }
}

但是上面的计算属性,函数存在一下问题的。

那就是,当关联的响应式数据,没有发生变化时,是不应该去重新执行计算的,而是只有当关联的数据发生改变时才会重新执行计算返回。

解决方案:

  1. 我们用一个变量表示是否需要重新执行计算 dirty
  2. 因为响应式数据一旦发生了改变,就会自动触发对应的副作用函数的重新执行
  3. 那么我需要在这个副作用函数执行完毕之后将 dirty 转换为 true就可以了

说白了,我们就是需要去监听也好,当与计算属性相关联的响应式数据发送改变,就改变dirty变量

修改我们的computed里的effect调用时,传递scheduler表示调度执行,说白了就是将最终的执行权力交给用户自己(我自己)

修改computed

let computed = (getter) => {
   let dirty = true, value;
   // dirty 表示是否需要重新计算执行
   // value 是表示计算结果
   let effectFn = effect(getter, {
       lazy: true,
       scheduler(fn){
           fn();
           // 当我们的副作用执行完毕之后,就需要将
           dirty = true; // 表示需要重新执行 
       }
   })
   let obj = {
      get value() {
      
        if (dirty) {
          // 只有当dirty为true时,才会重新执行
          value = effectFn();
          dirty = false;
          // 当执行了之后,就需要将dirty改为false,表示不需要重新执行
        }
        return value;
      }
   
   }
}

不好意思,这个实现还是存在一个小问题

那就是在 effect 中使用了计算属性,但是修改了响应式数据之后没有将副作用函数重新执行
什么意思呢?

let dataForm = computed(() => obj.name + obj.sex));

// 我们在effect中使用这个计算属性
effect(() => {
    console.log('此时没有重新执行');
    dataForm.value;
})
obj.name = 99;

当我们修改了,obj.name之后,effect参数没有重新执行。

仔细思考我们会发现,这是一个典型的effect嵌套问题

因为:

  1. obj这个响应式数据,的修改是会触发computed里面的effect副作用函数的执行
  2. 而外面的effect又没有对应的响应式数据与之关联

现在我们就需要手动触发,建立依赖关系,也需要手动触发响应

let computed = (getter) => {
   let dirty = true, value;
   // dirty 表示是否需要重新计算执行
   // value 是表示计算结果
   let effectFn = effect(getter, {
       lazy: true,
       scheduler(fn){
           fn();
           // 当我们的副作用执行完毕之后,就需要将
           dirty = true; // 表示需要重新执行 
           trigger(obj, 'value'); // 手动触发
       }
   })
   let obj = {
      get value() {
      
        if (dirty) {
          // 只有当dirty为true时,才会重新执行
          value = effectFn();
          dirty = false;
          // 当执行了之后,就需要将dirty改为false,表示不需要重新执行
        }
        track(obj, 'value'); // 其实这个value,是很随便的一个属性,意思是啥都行
        return value;
      }
   
   }
}

目的就是,让当前的的副作用函数,也就是外面的effect中的父作用函数,与obj响应式数据建立联系,

这里需要解决,嵌套effect时的副作用函数覆盖的问题因为此时的effect是没有写完整的,完整之后activeEffect变量就是最外层的副作用函数了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值