目标

  1. 使用方式与 ref 类似,用 .value
  2. 有一个缓存的强大功能

    happy path 目标

    类似 ref ,用 .value

    1. it('happy path', () => {
    2. const user = reactive({ age: 1 });
    3. const age = computed(() => {
    4. return user.age;
    5. });
    6. expect(age.value).toBe(1);
    7. });

    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); }

  1. <a name="OzTf2"></a>
  2. # 缓存功能目标
  3. ```typescript
  4. it('should compute lazily', () => {
  5. const value = reactive({ foo: 1 });
  6. const getter = vi.fn(() => {
  7. return value.foo;
  8. });
  9. const cValue = computed(getter);
  10. // lazy 懒执行
  11. expect(getter).not.toHaveBeenCalled();
  12. expect(cValue.value).toBe(1);
  13. expect(getter).toHaveBeenCalledTimes(1);
  14. // should not compute age
  15. cValue.value; // 再次触发 get,不会再计算
  16. expect(getter).toHaveBeenCalledTimes(1);
  17. // should not compute until needed 依赖的响应性对象发生改变
  18. value.foo = 2; // trigger -> effect,dirty 锁应该打开
  19. expect(getter).toHaveBeenCalledTimes(1);
  20. // now it should compute
  21. expect(cValue.value).toBe(2); // 重新执行
  22. expect(getter).toHaveBeenCalledTimes(2);
  23. // should not compute again
  24. cValue.value;
  25. expect(getter).toHaveBeenCalledTimes(2);
  26. });

缓存实现

不会再计算,返回缓存

第一次会把值缓存,之后就返回缓存

  1. class ComputedRefImpl {
  2. private _getter: any;
  3. private _dirty: boolean = true;
  4. // 缓存
  5. private _value: any;
  6. constructor(getter) {
  7. this._getter = getter;
  8. }
  9. get value() {
  10. // get 后上锁
  11. if(this._dirty) {
  12. this._dirty = false;
  13. this._value = this._getter();
  14. }
  15. return this._value;
  16. }
  17. }

当依赖的响应性对象发生改变

  1. // export 把 EffectReactive 暴露出去
  2. export class EffectReactive { // ... }
  1. class ComputedRefImpl {
  2. private _getter: any;
  3. private _dirty: boolean = true;
  4. private _value: any;
  5. private _effect: any;
  6. constructor(getter) {
  7. this._getter = getter;
  8. // 使用更低层的 ReactiveEffect 得到副作用
  9. this._effect = new ReactiveEffect(getter, ()=> {
  10. // 使用 scheduler 实现不会重复 get 和 重置缓存标记 _dirty
  11. if (!this._dirty) {
  12. this._dirty = true;
  13. }
  14. });
  15. }
  16. get value() {
  17. // 当依赖的响应性对象发生改变 dirty = true
  18. // 这里需要引入 EffectReactive
  19. if(this._dirty) {
  20. this._dirty = false;
  21. // this._value = this._getter();
  22. this._value = this._effect.run();
  23. }
  24. return this._value;
  25. }
  26. }