基本的 ref 目标

  1. it('happy path', () => {
  2. const a = ref(1);
  3. expect(a.value).toBe(1);
  4. });

实现

  1. class RefImpl {
  2. private _value: any;
  3. constructor(value) {
  4. this._value = value;
  5. }
  6. get value() {
  7. return this._value;
  8. }
  9. }
  10. export function ref(value) {
  11. return new RefImpl(value);
  12. }

ref 响应性目标

  1. it('should be reactive', () => {
  2. const a = ref(1);
  3. let dummy;
  4. let calls = 0;
  5. effect(() => {
  6. calls++;
  7. dummy = a.value;
  8. });
  9. expect(calls).toBe(1);
  10. expect(dummy).toBe(1);
  11. a.value = 2;
  12. expect(calls).toBe(2);
  13. expect(dummy).toBe(2);
  14. // same value should not trigger
  15. a.value = 2;
  16. expect(calls).toBe(2);
  17. expect(dummy).toBe(2);
  18. });

实现

  1. class RefImpl {
  2. private _value: any;
  3. // 用于收集依赖的容器
  4. public dep;
  5. constructor(value) {
  6. this._value = value;
  7. this.dep = new Set();
  8. }
  9. get value() {
  10. return this._value;
  11. }
  12. set value(newValue) {
  13. }
  14. }
  15. export function ref(value) {
  16. return new RefImpl(value);
  17. }

会用到 effect 收集依赖的部分,因为 ref 只有一个 value,对 effect 的 track 局部抽离为 trackEffect
同理触发依赖也是对 effect 的 trigger 局部抽离为 triggerEffect

  1. export function track(target, key) {
  2. if (!isTracking()) return;
  3. let depsMap = targetMap.get(target);
  4. if (!depsMap) {
  5. depsMap = new Map();
  6. targetMap.set(target, depsMap);
  7. }
  8. let dep = depsMap.get(key);
  9. if (!dep) {
  10. dep = new Set();
  11. depsMap.set(key, dep);
  12. }
  13. // 抽离依赖收集
  14. trackEffect(dep);
  15. }
  16. export function trackEffect(dep) {
  17. // 看盾 dep 之前有没能有添加过,添加过就不添加
  18. if (dep.has(activeEffect)) return;
  19. dep.add(activeEffect);
  20. activeEffect.deps.push(dep);
  21. }
  22. export function trigger(target, key) {
  23. const depsMap = targetMap.get(target);
  24. const dep = depsMap.get(key);
  25. // 抽离触发依赖
  26. triggerEffect(dep);
  27. }
  28. export function triggerEffect(dep) {
  29. for (const effect of dep) {
  30. if (effect.scheduler) {
  31. effect.scheduler();
  32. } else {
  33. effect.run();
  34. }
  35. }
  36. }
  37. export function isTracking() {
  38. return shouldTrack && activeEffect !== undefined;
  39. }
  1. import { trackEffects, triggerEffect } from './effect';
  2. class RefImpl {
  3. private _value: any;
  4. // 用于收集依赖的容器
  5. public dep;
  6. constructor(value) {
  7. this._value = value;
  8. this.dep = new Set();
  9. }
  10. get value() {
  11. // 依赖收集
  12. // 如果没有 effect 过会 trackEffects 中的 activeEffect 会为 undefined
  13. // 这里先作一个判断
  14. if (isTracking()) {
  15. trackEffects(this.dep);
  16. }
  17. return this._value;
  18. }
  19. set value(newValue) {
  20. // 如果 set 的时候,新旧值相同就不作处理
  21. if(Object.is(newValue, this._value)) return;
  22. this._value = newValue;
  23. // 触发依赖
  24. triggerEffects(this.dep);
  25. }
  26. }

重构

  1. set value(newValue) {
  2. // 如果 set 的时候,新旧值相同就不作处理
  3. if(hasChange(newValue, this._value) {
  4. this._value = newValue;
  5. // 触发依赖
  6. triggerEffects(this.dep);
  7. }
  8. }
  1. export const hasChange = (val, newValue) => {
  2. return !Object.is(val, newValue);
  3. };
  1. get value() {
  2. // 依赖收集
  3. trackRefValue(this);
  4. return this._value;
  5. }
  1. function trackRefValue(ref) {
  2. // 如果没有 effect 过会 trackEffects 中的 activeEffect 会为 undefined
  3. // 这里先作一个判断
  4. if (isTracking()) {
  5. trackEffect(ref.dep);
  6. }
  7. }

嵌套目标

  1. it('should make nested properties reactive', () => {
  2. const a = ref({
  3. count: 1,
  4. });
  5. let dummy;
  6. effect(()=>{
  7. dummy = a.value.count;
  8. });
  9. expect(dummy).toBe(1);
  10. a.value.count = 2;
  11. expect(dummy).toBe(2);
  12. });

实现

  1. class RefImpl {
  2. private _value: any;
  3. private _rawValue: any;
  4. public dep;
  5. constructor(value) {
  6. this._rawValue = value;
  7. // 看看 value 是不是对象
  8. this._value = isObject(value) ? reactive(value) : value;
  9. this.dep = new Set();
  10. }
  11. get value() {
  12. trackRefValue(this);
  13. return this._value;
  14. }
  15. set value(newValue) {
  16. // 这里对比会有问题,为因为会转为 proxy,所以要保留未 reactive 的对象
  17. // if (hasChange(newValue, this._value)) {
  18. if (hasChange(newValue, this._rawValue)) {
  19. // 保留未 reactive 的对象
  20. this._rawValue = newValue;
  21. this._value = isObject(newValue) ? reactive(newValue) : newValue;
  22. triggerEffect(this.dep);
  23. }
  24. }
  25. }

重构

抽离 isObject(value) ? reactive(value) : value

  1. class RefImpl {
  2. private _value: any;
  3. private _rawValue: any;
  4. public dep;
  5. constructor(value) {
  6. this._rawValue = value;
  7. // 看看 value 是不是对象
  8. this._value = covert(value);
  9. this.dep = new Set();
  10. }
  11. get value() {
  12. trackRefValue(this);
  13. return this._value;
  14. }
  15. set value(newValue) {
  16. // 这里对比会有问题,为因为会转为 proxy,所以要保留未 reactive 的对象
  17. // if (hasChange(newValue, this._value)) {
  18. if (hasChange(newValue, this._rawValue)) {
  19. // 保留未 reactive 的对象
  20. this._rawValue = newValue;
  21. this._value = covert(newValue);
  22. triggerEffect(this.dep);
  23. }
  24. }
  25. }
  26. function convert(value) {
  27. return isObject(value) ? reactive(value) : value;
  28. }