目标

处理一些关于 stop 的 edge case
在 obj.prop ++ 时,会同时触发 getter 和 setter,因为 getter 会导致重新依赖收集。导致 stop 失败

  1. it('stop', () => {
  2. let dummy;
  3. const obj = reactive({ foo: 1 });
  4. const runner = effect(() => {
  5. dummy = obj.prop;
  6. });
  7. obj.prop = 2;
  8. expect(dummy).toBe(2);
  9. stop(runner);
  10. obj.prop = 3;
  11. expect(dummy).toBe(2);
  12. // ******* bug
  13. obj.prop ++; // obj.prop = obj.prop + 1; get -> set
  14. expect(dummy).toBe(2);
  15. // stopped effect should still be manually callable
  16. runner();
  17. expect(dummy).toBe(4);
  18. });

优化

可以对依赖收集 track 时做一些文章

  1. let activeEffect;
  2. let shouldTrack;
  3. class EffectReactive {
  4. private _fn;
  5. deps = [];
  6. active = true;
  7. onStop?: () => void;
  8. constructor(fn, public scheduler?) {
  9. this._fn = fn;
  10. }
  11. run() {
  12. // 1. 会收集依赖
  13. // 使用 shouldTrack 来区分
  14. if (!this.active) {
  15. return this._fn();
  16. }
  17. shouldTrack = true;
  18. activeEffect = this;
  19. const result = this._fn();
  20. // reset
  21. shouldTrack = false;
  22. return result;
  23. }
  24. stop() {
  25. if (this.active) {
  26. cleanupEffect(this);
  27. if (this.onStop) {
  28. this.onStop();
  29. }
  30. this.active = false;
  31. }
  32. }
  33. }
  34. function cleanupEffect(effect) {
  35. effect.deps.forEach((dep: any) => {
  36. dep.delete(effect);
  37. });
  38. // dep 都清空了,对应的 effect.deps 就没有意义,可清空作优化
  39. effect.deps.length = 0;
  40. }
  41. export function track(target, key) {
  42. // 重构
  43. // if (!activeEffect) return;
  44. // if (!shouldTrack) return;
  45. if(!isTracking()) return;
  46. let depsMap = targetMap.get(target);
  47. if (!depsMap) {
  48. depsMap = new Map();
  49. targetMap.set(target, depsMap);
  50. }
  51. let dep = depsMap.get(key);
  52. if (!dep) {
  53. dep = new Set();
  54. depsMap.set(key, dep);
  55. }
  56. // 已经在 dep 中
  57. if (dep.has(activeEffect)) return;
  58. dep.add(activeEffect);
  59. activeEffect.deps.push(dep);
  60. }
  61. function isTracking() {
  62. return shouldTrack && activeEffect !== undefined;
  63. }