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);
2.原理:脏值检测机制**
内部保存一个变量dirty
,用来判断是否需要重新执行。默认值为true
,需要执行,计算取值后,缓存结果,dirty
改为false
,后续取值判断为false
,直接取缓存的值,依赖的属性发生变化后,dirty
改为true
3.实现
console.log(aliasName)
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.name
,effect
收集了计算属性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文件,抽出来方法trackEffects
和triggerEffects
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();
}
}
});
}