计算属性本质上就是一个函数,如果内部使用响应式数据,内部会做依赖收集,会触发依赖更新(
effect),他会将计算结果进行缓存,直到响应式数据改变进行重新计算。
计算属性实现
import { ReactiveEffect } from "./effect";
class ComputedImpl {
private _getter: Function;
private _dirty: boolean = true;
private _value: any;
private _effect: any;
constructor(_getter: Function) {
this._getter = _getter;
this._effect = new ReactiveEffect(_getter, () => {
if (!this._dirty) {
this._dirty = true;
}
});
}
get value() {
if (this._dirty) {
this._dirty = false;
this._value = this._effect.run();
}
return this._value;
}
}
function computed(getter) {
return new ComputedImpl(getter);
}
计算属性内部执行流程
- 首先用户创建一个计算属性
const value = reactive({
foo: 1,
});
const getter = () => {
return value.foo;
);
const cValue = computed(getter);
- 内部计算属性进行构造函数初始化
class ComputedImpl {
private _getter: Function;
private _dirty: boolean = true;
private _value: any;
private _effect: any;
constructor(_getter: Function) {
this._getter = _getter;
this._effect = new ReactiveEffect(_getter, () => {
if (!this._dirty) {
this._dirty = true;
}
});
}
}
因为考虑到计算属性内部匿名函数内当响应式数据发生变化需要调用getter函数计算最新值,所以采用ReactiveEffect,此时_getter便是effect副作用函数,同时传递匿名函数为schedular参数,每次被调用将_dirty设置为true这是一个求值开关
- 当用户使用计算属性求值时,也就是访问计算属性value时会触发该函数
get value() {
if (this._dirty) {
this._dirty = false;
this._value = this._effect.run();
}
return this._value;
}
当首次使用计算属性时_dirty就为true,然后调用传入的依赖也就是用户的_getter函数,最后进行返回值,当多次使用计算属性时,而内部响应式变量没有发生改变也不会重新进行求值,会直接拿取上一次缓存的变量