vue3源码(三)computed

1.功能

接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 ref 对象。
默认不执行,在取值时执行,具有缓存功能,数据不变多次取值只触发一次取值计算。

import {
        reactive,
        effect,
        computed,
      } from "/node_modules/@vue/reactivity/dist/reactivity.esm-browser.js";
      const state = reactive({ name: "orange" });
      let aliasName = computed(() => {
        console.log("执行计算方法");
        return "_" + state.name;
      });
      console.log("开始取值111");
      console.log(aliasName.value);
      console.log(aliasName.value);
      console.log(aliasName.value);
      state.name = "apple";
      console.log(aliasName.value);

vue3源码(三)computed_第1张图片

2.原理:脏值检测机制**

内部保存一个变量dirty,用来判断是否需要重新执行。默认值为true,需要执行,计算取值后,缓存结果,dirty改为false,后续取值判断为false,直接取缓存的值,依赖的属性发生变化后,dirty改为true

3.实现

console.log(aliasName)
vue3源码(三)computed_第2张图片

import { isFunction } from "@vue/shared";
import { ReactiveEffect } from "./effect";
const noop = () => {};

class ComputedRefImpl {
  dep = undefined;
  effect = undefined;
  __v_isRef = true; // 代表是ref,需要用.value 取值
  _dirty = true;
  _value;
  constructor(getter, public setter) {
    // 此处不能使用effect 会立即执行,ReactiveEffect可以手动调用run方法执行
    this.effect = new ReactiveEffect(getter, () => {
      // 此方法为getter依赖的属性变化后执行的方法:scheduler
      this._dirty = true;
    });
  }

  get value() {
    if (this._dirty) {
      this._value = this.effect.run();
      this._dirty = false;
    }
    return this._value;
  }

  set value(value: any) {
    this.setter(value);
  }
}

export function computed(getterOrOptions) {
  let onlyGetter = isFunction(getterOrOptions);
  let getter;
  let setter;
  if (onlyGetter) {
    getter = getterOrOptions;
    setter = noop;
  } else {
    getter = getterOrOptions.get;
    setter = getterOrOptions.set;
  }
  return new ComputedRefImpl(getter, setter);
}

此时已经简单实现了computed,以下情况我们修改了state.name的值,理论上应该执行effect重新渲染页面,但是并未执行

const state = reactive({
        name: "orange",
      });
      let aliasName = computed(() => {
        console.log("执行计算方法");
        return "_" + state.name;
      });

      effect(() => {
        app.innerHTML = aliasName.value;
      });

      setTimeout(() => {
        state.name = "apple";
      }, 1000);

计算属性aliasName收集了state.nameeffect收集了计算属性aliasName,同时让计算属性收集effect,当计算属性依赖的属性变化后,不仅修改dirty值,同时触发effect

 get value() {
    // 证明是在effect中使用的
    if (activeEffect) {
      trackEffects(this.dep || (this.dep = new Set()));
    }
    if (this._dirty) {
      this._value = this.effect.run();
      this._dirty = false;
    }
    return this._value;
  }
this.effect = new ReactiveEffect(getter, () => {
      // 此方法为getter依赖的属性变化后执行的方法:scheduler
      this._dirty = true;
      triggerEffects(this.dep);
    });

调整effect.ts文件,抽出来方法trackEffectstriggerEffects

const targetMap = new WeakMap();
export function track(target, key) {
  // 如果取值操作没有发生在effect中,则不需要收集依赖
  if (!activeEffect) {
    return;
  }
  // 判断是否已经记录过
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()));
  }
  let dep = depsMap.get(key);
  if (!dep) {
    // 值为一个不重复数组
    depsMap.set(key, (dep = new Set()));
  }
  trackEffects(dep);
}

export function trackEffects(dep) {
  let shouldTrack = !dep.has(activeEffect);
  if (shouldTrack) {
    dep.add(activeEffect);
    activeEffect.deps.push(dep); //同时effect记住当前这个属性
  }
}

export function trigger(target, key, newValue, oldValue) {
  // 通过对象找到对应的属性 让这个属性对应的effect重新执行

  const depsMap = targetMap.get(target);
  if (!depsMap) {
    return;
  }
  const dep = depsMap.get(key); // name 或者 age对应的所有effect

  triggerEffects(dep);
}

export function triggerEffects(dep) {
  const effects = [...dep];
  // 运行的是数组 删除的是set
  effects &&
    effects.forEach((effect) => {
      // 正在执行的effect ,不要多次执行
      if (effect !== activeEffect) {
        if (!effect.scheduler) {
          effect.run();
        } else {
          effect.scheduler();
        }
      }
    });
}

你可能感兴趣的:(vue3源码,vue.js,javascript,前端)