禁止转载,侵权必究!
Computed 实现原理
接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 ref 对象。
- 计算属性的 getter 只有当取值时才会执行。
- 计算属性是具备缓存的,如果依赖的值不发生变化,不会重新执行 getter。dirty 变量来控制默认 true;
- 计算属性也是一个 effect,内部也具备依赖收集的功能
computed.html script
import { reactive, effect, computed } from "./reactivity.esm.js";
const state = reactive({ flag: true, name: "jw", age: 30 });
const aliasName = computed(() => {
console.log("computed-run");
return "**" + state.name + "**";
});
const runner = effect(() => {
console.log("effect-run");
console.log(aliasName.value);
console.log(aliasName.value);
console.log(aliasName.value);
});
setTimeout(() => {
state.name = "Mr Jiang";
}, 1000);
1.计算属性的实现
computed.ts
class ComputedRefImpl {
public effect; // 计算属性是基于effect实现的
public _value; // 缓存计算属性的执行结果
public dep; // 记录依赖的effect
public __v_isRef = true; // 计算属性是一个ref
public _dirty = true; // 标识此computed依赖的值是否发生变化
constructor(getter, public setter) {
// 创建 ReactiveEffect 时,传入scheduler函数,稍后依赖的属性变化时调用此方法!
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) {
// 依赖的值变化更新dirty并触发更新
this._dirty = true;
triggerEffects(this.dep);
}
});
}
// 类的属性访问器 Object.defineProperty(实例,value,{get,set})
get value() {
// 取值的时候进行依赖收集
if (activeEffect) {
trackEffects(this.dep || (this.dep = new Set()));
}
if (this._dirty) {
// 如果是脏值, 执行函数
this._dirty = false;
this._value = this.effect.run();
}
return this._value;
}
set value(newValue) {
this.setter(newValue);
}
}
const noop = () => {};
export function computed(getterOrOptions) {
const onlyGetter = isFunction(getterOrOptions); // 传入的是函数就是getter;
let getter;
let setter;
if (onlyGetter) {
getter = getterOrOptions;
setter = noop;
} else {
getter = getterOrOptions.get;
setter = getterOrOptions.set || noop;
}
// 创建计算属性
return new ComputedRefImpl(getter, setter);
}
2.收集依赖的 effect
effect.ts
export function trackEffects(dep) {
// 收集dep 对应的effect
let shouldTrack = !dep.has(activeEffect);
if (shouldTrack) {
dep.add(activeEffect);
activeEffect.deps.push(dep);
}
}
3.触发依赖的 effect 更新
effect.ts
export function triggerEffects(dep) {
if (dep) {
const effects = [...dep];
effects.forEach((effect) => {
if (effect !== activeEffect) {
if (effect.scheduler) {
// 如果有调度函数则执行调度函数
effect.scheduler();
} else {
effect.run();
}
}
});
}
}