vue3-响应式高级篇(二)

167 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情

这篇主要是对计算属性computed的实现,主要是在effect的基础上实现的。 api用法:v3.cn.vuejs.org/api/compute…

源码:github1s.com/vuejs/core/…

// 用法1  传函数 只能get
let f = computed(()=>{
    return state.a+state.b
})

// 2 接受一个具有 `get` 和 `set` 函数的对象,用来创建可写的 ref 对象
let f = computed({
    get(){
        return state.a + state.b
    },
    set(value){
        state.a = value
    }
})
f.value = 'a'
// 例子
effect(()=>{
    app.innerHTML = f.value
})

特性:

响应式更新(effect中有用到计算属性的,当计算属性里的依赖属性变化时会重新执行effect);

结果缓存;

惰性求值(真正用到的时候才去计算)

流程

判断传入参数是对象还是函数,得到相应的getset,返回构造类ComputedRefImpl

构造函数中 构造一个effect实例,

类中定义一个变量dirty,默认为true,当需要获取这个computed值时,走的是get,检测dirty,为真时去执行computer里的函数,得到值赋值给value进行缓存、将dirty设为false并返回value,下次拿的时候由于dirty为false,直接拿缓存即可。

get中同响应式一样,设置变量dep存储effect依赖数组,如例子的

()=>{
    app.innerHTML = f.value
}

effect实例的scheduler回调中,可以监听依赖属性变动,触发回调时,dirtyfalse则要改成true,去执行effect依赖数组。dirtytrue才能让computed值获取时拿到最新的。

源码

import {
  isTracking,
  ReactiveEffect,
  trackEffects,
  triggerEffects,
} from "./effect";

function isFunction(val) {
  return typeof val == "function";
}
export function computed(getterOrOptions) {
  // 两种用法,一种是传入函数,一种是传入对象
  const onlyGetter = isFunction(getterOrOptions);
  let getter;
  let setter;
  if (onlyGetter) {
    getter = getterOrOptions;
    // 不给赋值
    setter = () => {};
  } else {
    getter = getterOrOptions.get;
    setter = getterOrOptions.set;
  }

  return new ComputedRefImpl(getter, setter);
}
class ComputedRefImpl {
  public dep; // this.dep = undefined;
  // 脏数据 初始化或者属性变动的时候为true,用来确保缓存的值是最新的
  public _dirty = true; // this._dirty = true;
  public effect; // 计算属性是依赖于effect的
  public _value;
  constructor(getter, public setter) {
    // 只有调用computed()才执行一次
    // 这里将计算属性包成一个effect,当里面的属性变动时会触发回调
    this.effect = new ReactiveEffect(getter, () => {
      // 稍后计算属性依赖的值变化 改为调用此函数
      if (!this._dirty) {
        this._dirty = true;
        // 执行依赖数组们
        triggerEffects(this.dep);
      }
    });
  }
  get value() {
    // 取值时会走get方法
    if (isTracking()) {
      // 收集effect依赖数组
      trackEffects(this.dep || (this.dep = new Set()));
    }
    if (this._dirty) {
      // 将结果缓存到this._value 这样就不用每次都run了
      this._value = this.effect.run();
      this._dirty = false;
    }
    return this._value;
  }
  set value(newValue) {
    this.setter(newValue); // 如果修改计算属性的值 就触发你自己的set方法
  }
}