reactive

大约 1 分钟

前言

上一节实现的类无法实现对引用类型数据的操作捕获,所以这一节使用 Proxy 来实现对引用类型数据的操作捕获。

Vue2 使用的是 Object.defineProperty实现

Vue3 使用的是 Proxy实现

dep

修改一下上一节实现的 dep 类,移除 value 属性和 getter setter

let activeEffect;

class Dep {
  subscribers = new Set();
  depend() {
    if (activeEffect) {
      this.subscribers.add(activeEffect);
    }
  }
  notify() {
    this.subscribers.forEach((effect) => {
      effect();
    });
  }
}

reactive

在 vue3 中,reactive 函数接收的是一个引用类型的数据,使之具有响应式。

思路

接收引用类型的数据;
返回数据的代理;

实现

// WeakMap:只接受 object 作为 key;当 key 不被引用时,该 key 也就随之清除(垃圾回收)
const targetsMap = new WeakMap(); // 记录所有响应式对象的依赖

/**
 * @description: 获取指定值的 Dep 实例(记录着该值的所有依赖)
 * @param target 目标对象
 * @param key 键
 * @return {object} dep
 */
function getDep(target, key) {
  let depsMap = targetsMap.get(target);
  if (!depsMap) {
    depsMap = new Map();
    targetsMap.set(target, depsMap);
  }
  let dep = depsMap.get(key);
  if (!dep) {
    dep = new Dep();
    depsMap.set(key, dep);
  }
  return dep;
}

// 拦截器
const reactiveHandlers = {
  // receiver 解决 this 指向问题
  get(target, key, receiver) {
    const dep = getDep(target, key);
    // 收集依赖
    dep.depend();

    return Reflect.get(target, key, receiver);
  },
  set(target, key, value, receiver) {
    const result = Reflect.set(target, key, value, receiver);
    const dep = getDep(target, key);
    // 触发依赖
    dep.notify();
    return result;
  },
};

/**
 * @description: 创建响应式对象
 * @param raw 要代理的对象
 * @return {object} proxy
 */
function reactive(raw) {
  return new Proxy(raw, reactiveHandlers);
}

创建响应式对象

const state = reactive({
  count: 1,
});

watchEffect(() => {
  console.log(state.count);
});

state.count++; // 2

总结

响应式的原理

  1. 访问属性时收集依赖
  2. 修改属性时触发依赖