最小化实现 vue3 响应 原理

3 阅读1分钟

测试


import { effect } from "./effect.mjs";
import { reactive } from "./reactive.mjs";

const obj = { a: 1, b: 2 };

const proxyObj = reactive(obj);

effect(() => {
  console.log("obj.a", proxyObj.a);
});

effect(() => {
  console.log("obj.b", proxyObj.b);
});

proxyObj.a = 2;


打印结果

obj.a 1
obj.b 2
obj.a 2

reactive

基于 proxy 在 get set 方法中收集和触发依赖

// reactive.mjs

import { track, trigger } from "./dep.mjs";

const reactiveMap = new WeakMap();

export function reactive(target) {
  const res = new Proxy(target, {
    get(target, key) {
      track(target, key);
      return Reflect.get(target, key);
    },
    set(target, key, value) {
      const result = Reflect.set(target, key, value);
      trigger(target, key);
      return result;
    },
  });
  reactiveMap.set(target, res);
  return res;
}

dep

维护 obj 和 执行函数的依赖关系

// dep.mjs

import { activeSub } from "./effect.mjs";
const targetMap = new WeakMap();

class Dep {
  map;
  key;
  constructor() {}

  track() {
    // 当前正在执行的函数
    this.sub = activeSub;
  }

  trigger() {
    this.sub.notify();
  }
}

// 收集依赖
export function track(target, key) {
  let depsMap = targetMap.get(target);

  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()));
  }
  let dep = depsMap.get(key);

  if (!dep) {
    depsMap.set(key, (dep = new Dep()));
    dep.map = depsMap;
    dep.key = key;
  }

  dep.track();
}

// 触发依赖
export function trigger(target, key) {
  let depsMap = targetMap.get(target);

  const dep = depsMap.get(key);

  dep.trigger();
}

effect

// effect.mjs

export let activeSub;

class ReactiveEffect {
  constructor(fn) {
    this.fn = fn;
  }

  run() {
    activeSub = this;
    this.fn();
  }
  notify() {
    this.fn();
  }
}

export function effect(fn) {
  const e = new ReactiveEffect(fn);

  e.run();

  return e.run.bind(e);
}