目标
- 使用方式与 ref 类似,用 .value
-
happy path 目标
类似 ref ,用 .value
it('happy path', () => {
const user = reactive({ age: 1 });
const age = computed(() => {
return user.age;
});
expect(age.value).toBe(1);
});
happy path 实现
```typescript class ComputedRefImpl { private _getter: any; constructor(getter) { this._getter = getter; }
get value() { return this._getter(); } }
export function computed(getter) { return new ComputedRefImpl(getter); }
<a name="OzTf2"></a>
# 缓存功能目标
```typescript
it('should compute lazily', () => {
const value = reactive({ foo: 1 });
const getter = vi.fn(() => {
return value.foo;
});
const cValue = computed(getter);
// lazy 懒执行
expect(getter).not.toHaveBeenCalled();
expect(cValue.value).toBe(1);
expect(getter).toHaveBeenCalledTimes(1);
// should not compute age
cValue.value; // 再次触发 get,不会再计算
expect(getter).toHaveBeenCalledTimes(1);
// should not compute until needed 依赖的响应性对象发生改变
value.foo = 2; // trigger -> effect,dirty 锁应该打开
expect(getter).toHaveBeenCalledTimes(1);
// now it should compute
expect(cValue.value).toBe(2); // 重新执行
expect(getter).toHaveBeenCalledTimes(2);
// should not compute again
cValue.value;
expect(getter).toHaveBeenCalledTimes(2);
});
缓存实现
不会再计算,返回缓存
第一次会把值缓存,之后就返回缓存
class ComputedRefImpl {
private _getter: any;
private _dirty: boolean = true;
// 缓存
private _value: any;
constructor(getter) {
this._getter = getter;
}
get value() {
// get 后上锁
if(this._dirty) {
this._dirty = false;
this._value = this._getter();
}
return this._value;
}
}
当依赖的响应性对象发生改变
// export 把 EffectReactive 暴露出去
export class EffectReactive { // ... }
class ComputedRefImpl {
private _getter: any;
private _dirty: boolean = true;
private _value: any;
private _effect: any;
constructor(getter) {
this._getter = getter;
// 使用更低层的 ReactiveEffect 得到副作用
this._effect = new ReactiveEffect(getter, ()=> {
// 使用 scheduler 实现不会重复 get 和 重置缓存标记 _dirty
if (!this._dirty) {
this._dirty = true;
}
});
}
get value() {
// 当依赖的响应性对象发生改变 dirty = true
// 这里需要引入 EffectReactive
if(this._dirty) {
this._dirty = false;
// this._value = this._getter();
this._value = this._effect.run();
}
return this._value;
}
}