目标

reactive 方法创建一个响应性对象

  • 响应式对象里面有一个容器,用于存放收集的依赖

effect 方法接收一个函数 fn

  • effect 用于收集依赖

track 依赖收集

  • fn 一开始会调用,即出发响应性对象的 get
  • 当响应性对象的 get 触发就会收集 fn 到其容器

trigger 触发依赖

  • 当响应性对象 set 触发时,会把容器中所有的 fn 依赖进行调用

effect 的 happy path

  1. describe('effect', () => {
  2. it('happy path', () => {
  3. const user = reactive({
  4. age: 10,
  5. });
  6. let nextAge;
  7. effect(() => {
  8. nextAge = user.age + 1;
  9. });
  10. expect(nextAge).toBe(11);
  11. user.age++;
  12. expect(nextAge).toBe(12);
  13. });
  14. });

基于 effect,拆分任务先实现 reactive 的 happy path

  1. import { reactive } from '../reactive';
  2. describe('reactive', () => {
  3. it('happy path', () => {
  4. const original = { foo: 1 };
  5. const observed = reactive(original);
  6. expect(observed).not.toBe(original);
  7. expect(observed.foo).toBe(1);
  8. });
  9. });

reactive 实现

reactive 的本质是一个 proxy 代理,对其 get / set 进行拦截

  1. export function reactive(raw) {
  2. return new Proxy(raw, {
  3. get(target, key) {
  4. const res = Reflect.get(target, key);
  5. // TODO 依赖收集
  6. return res;
  7. },
  8. set(target, key, value) {
  9. const res = Reflect.set(target, key, value);
  10. // TODO 触发依赖
  11. return res;
  12. },
  13. });
  14. }

这样 reactive happy path 已经通过

effect 实现

  1. // 抽离出一个 class 进行封装响应性的操作
  2. class EffectReactive {
  3. private _fn: any;
  4. constructor(fn) {
  5. this._fn = fn;
  6. }
  7. run() {
  8. this._fn();
  9. }
  10. }
  11. export function effect(fn) {
  12. const effectReactive = new EffectReactive(fn);
  13. // effect 提供一个 run 的方法,来调用 fn
  14. effectReactive.run();
  15. }

依赖收集实现

使用当前的被代理的对象 target 从 targetMap 中取出 depsMap, 再由 key 从 depsMap 取出容器 dep。然后就可以添加到 dep 中。
实现 effect %26 reactive %26 依赖收集 %26 触发依赖 - 图1
把 fn 保存到容器中

  1. export function reactive(raw) {
  2. return new Proxy(raw, {
  3. get(target, key) {
  4. const res = Reflect.get(target, key);
  5. track(target, key);
  6. return res;
  7. },
  8. });
  9. }
  1. class EffectReactive {
  2. private _fn;
  3. constructor(fn) {
  4. this._fn = fn;
  5. }
  6. run() {
  7. // 保存当前的 effect
  8. activeEffect = this;
  9. this._fn();
  10. }
  11. }
  12. // 通过全局变量,把当前的 effect 保存到 activeEffect
  13. let activeEffect;
  14. const targetMap = new Map();
  15. export function track(target, key) {
  16. let depsMap = targetMap.get(target);
  17. // 初始化当没有
  18. if (!depsMap) {
  19. depsMap = new Map();
  20. targetMap.set(target, depsMap);
  21. }
  22. let dep = depsMap.get(key);
  23. if (!dep) {
  24. dep = new Set();
  25. depsMap.set(key, dep);
  26. }
  27. dep.add(activeEffect);
  28. }

触发依赖

  1. export function trigger(target, key) {
  2. const depsMap = targetMap.get(target);
  3. const dep = depsMap.get(key);
  4. for (const effect of dep) {
  5. effect.run();
  6. }
  7. }
  1. import { track, trigger } from './effect';
  2. export function reactive(raw) {
  3. return new Proxy(raw, {
  4. get(target, key) {
  5. const res = Reflect.get(target, key);
  6. track(target, key);
  7. return res;
  8. },
  9. set(target, key, value) {
  10. const res = Reflect.set(target, key, value);
  11. trigger(target, key);
  12. return res;
  13. },
  14. });
  15. }