vue3的响应式原理

123 阅读3分钟

基础概念

Vue 3 引入了一套新的响应式系统,该系统基于 ES6 的 Proxy 对象,提供了一个更强大、更灵活的方式来处理响应式数据。

Proxy 对象

在 Vue 3 中,当使用 reactiveref 创建响应式对象时,实际上返回的是一个通过 Proxy 创建的对象。Proxy 允许我们拦截和定义自定义行为,如属性的读取 (get) 和设置 (set)。

创建响应式对象
Javascript
深色版本
const state = reactive({
  count: 0
});

这里 state 是一个响应式的对象,对它的任何属性的访问和修改都会被代理对象捕获。

Proxy 拦截器

在创建代理对象时,我们可以指定一系列的拦截器,用于处理各种操作。Vue 3 主要关注以下几种拦截器:

  • get: 当访问一个属性时触发。
  • set: 当设置一个属性时触发。
  • deleteProperty: 当删除一个属性时触发。
  • has: 当使用 in 操作符时触发。
  • ownKeys: 当枚举对象的键时触发。

跟踪依赖

Vue 3 使用一个叫做 effect 的概念来跟踪依赖关系。effect 是一个函数,当它运行时,它会收集依赖项。当依赖项发生变化时,effect 会自动重新执行。

创建 Effect
Javascript
深色版本
let count = 0;
effect(() => {
  document.title = `Count is ${count}`;
});

// 初始渲染
count = 1; // 更新计数

在这个例子中,effect 会在第一次调用时收集 count 的依赖,并将其存储起来。当 count 发生变化时,effect 会自动重新执行。

触发更新

当响应式数据发生变化时,Vue 3 的响应式系统会触发更新。这通常涉及两个步骤:

  1. 追踪依赖 (track) :当访问响应式数据时,Vue 会记录哪些 effect 依赖于这些数据。
  2. 触发更新 (trigger) :当响应式数据发生变化时,Vue 会通知所有依赖这些数据的 effect 重新执行。
示例代码
Javascript
深色版本
function track(target, key) {
  if (!shouldTrack || !activeEffect) return;
  let depsMap = targetMap.get(target) || targetMap.set(target, (depsMap = new Map()));
  let dep = depsMap.get(key) || depsMap.set(key, (dep = new Set()));
  if (!dep.has(activeEffect)) {
    dep.add(activeEffect);
    activeEffect.deps.push(dep);
  }
  trackEffects(dep);
}

function trigger(target, key) {
  const dep = targetMap.get(target).get(key);
  if (dep) {
    triggerEffects(dep);
  }
}

function trackEffects(effect) {
  // 标记当前 effect 为跟踪状态
  // 这里省略具体实现细节
}

function triggerEffects(effect) {
  // 执行 effect 的回调函数
  // 这里省略具体实现细节
}

ref 与 reactive 的区别

除了使用 reactive 来创建响应式对象外,Vue 3 还提供了 ref 用于创建基本类型的响应式变量。ref 返回的是一个对象,其中包含一个名为 value 的属性,这个属性才是响应式的。

Javascript
深色版本
const count = ref(0);
console.log(count.value); // 输出 0
count.value++; // 更新值

使用 ref 使得基本类型的数据也能拥有响应式的能力,这对于状态管理非常有用。

总结

Vue 3 的响应式系统是一个强大的工具,它利用了现代 JavaScript 特性(如 Proxy)来简化数据绑定和响应式逻辑。通过 reactiveref 创建响应式数据结构,以及使用 effect 来自动追踪依赖和触发更新,使得 Vue 3 的响应式系统既强大又易于使用。