Vue 3 的响应式原理

Vue 3 的响应式原理可以比喻为"智能监控系统":当数据变化时,它能自动追踪依赖关系并触发更新。以下是通俗解释和核心机制:


一、核心原理:Proxy 代理

Vue 3 的响应式系统基于 JavaScript 的 Proxy 对象实现(Vue 2 使用 Object.defineProperty)。
Proxy 的优势

  • 支持动态新增属性 (无需 Vue.set
  • 支持数组索引和长度变化
  • 性能更好(无需递归遍历对象)

二、实现步骤:如何让数据"活"起来?

1. 创建响应式对象

reactive() 包裹普通对象,返回一个 Proxy 代理对象:

javascript 复制代码
import { reactive } from 'vue';

const state = reactive({
  count: 0,
  list: ['Apple', 'Banana']
});

Proxy 的拦截行为

  • 当你访问 state.countget 操作)→ 收集依赖(记录谁在用这个值)。
  • 当你修改 state.countset 操作)→ 触发更新(通知依赖它的地方更新)。

2. 依赖收集(Tracking Dependencies)

当模板或计算属性访问响应式数据时,Vue 会记录这个关系:

vue 复制代码
<template>
  <div>{{ state.count }}</div>  <!-- 访问 state.count → 建立依赖 -->
</template>

原理

  • 通过 effect 函数(类似"监听器")包裹需要响应式执行的代码。
  • 当代码执行时,所有被访问的响应式属性会自动收集当前 effect 作为依赖。

3. 触发更新(Triggering Updates)

当数据变化时,Proxy 的 set 拦截器会找到所有关联的 effect 并重新执行它们:

javascript 复制代码
state.count = 10; // 触发 set → 通知所有依赖的 effect 更新

三、核心 API 的差异

1. reactive vs ref
  • reactive:针对对象(Object、Array)的深度代理。

    javascript 复制代码
    const obj = reactive({ a: 1 });
  • ref :针对基本类型(string、number、boolean),通过 .value 访问。

    javascript 复制代码
    const count = ref(0);
    console.log(count.value); // 0

    为什么需要 ref

    Proxy 无法直接代理基本类型,ref 将值包装在 { value: ... } 对象中,再对这个对象做响应式处理。


2. effect 函数

Vue 内部通过 effect 实现依赖追踪和更新触发(开发者通常通过 watchcomputed 间接使用):

javascript 复制代码
import { effect } from 'vue';

effect(() => {
  console.log('count变化了:', state.count); // 自动追踪 state.count
});

四、简单实现(极简版)

javascript 复制代码
// 极简响应式系统
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      track(target, key); // 收集依赖
      return target[key];
    },
    set(target, key, value) {
      target[key] = value;
      trigger(target, key); // 触发更新
      return true;
    }
  });
}

// 依赖收集和触发
let activeEffect = null;
const depsMap = new Map();

function track(target, key) {
  if (activeEffect) {
    let dep = depsMap.get(target)?.get(key);
    if (!dep) {
      dep = new Set();
      depsMap.set(target, (depsMap.get(target) || new Map()).set(key, dep));
    }
    dep.add(activeEffect);
  }
}

function trigger(target, key) {
  const dep = depsMap.get(target)?.get(key);
  dep?.forEach(effect => effect());
}

function effect(fn) {
  activeEffect = fn;
  fn(); // 首次执行以收集依赖
  activeEffect = null;
}

五、关键特点

  1. 嵌套对象自动代理
    reactive 会递归代理对象的所有嵌套属性。

  2. 懒代理

    Vue 3 不会立即代理所有属性,只有被访问到的属性才会被代理(性能优化)。

  3. 数组处理优化

    直接通过索引修改数组或修改 length 属性也能触发更新。


六、开发者注意事项

  1. 避免解构响应式对象

    解构会破坏代理关系,导致失去响应性:

    javascript 复制代码
    const { count } = state; // ❌ 错误!count 不再是响应式的
    const count = ref(state.count); // ✅ 正确
  2. 使用 toRefs 保持响应性

    解构时保持响应式:

    javascript 复制代码
    import { toRefs } from 'vue';
    const { count } = toRefs(state); // 通过 .value 访问

七、总结

Vue 3 的响应式系统像一个"智能摄像头":

  1. 监控数据访问get):记录谁在关注这个数据。
  2. 监控数据修改set):通知所有关注者更新。
  3. 自动化管理依赖:开发者只需关注数据变化,视图自动同步。

通过 Proxy 的强大能力,Vue 3 解决了 Vue 2 的诸多限制(如数组和新增属性的响应问题),同时提升了性能和开发体验。

相关推荐
萌萌哒草头将军6 小时前
⚡⚡⚡尤雨溪宣布开发 Vite Devtools,这两个很哇塞 🚀 Vite 的插件,你一定要知道!
前端·vue.js·vite
游离状态的猫16 小时前
JavaScript性能优化实战:从瓶颈定位到极致提速
开发语言·javascript·性能优化
小彭努力中6 小时前
7.Three.js 中 CubeCamera详解与实战示例
开发语言·前端·javascript·vue.js·ecmascript
浪裡遊7 小时前
跨域问题(Cross-Origin Problem)
linux·前端·vue.js·后端·https·sprint
滿7 小时前
Vue3 Element Plus el-tabs数据刷新方法
javascript·vue.js·elementui
LinDaiuuj7 小时前
判断符号??,?. ,! ,!! ,|| ,&&,?: 意思以及举例
开发语言·前端·javascript
敲厉害的燕宝7 小时前
Pinia——Vue的Store状态管理库
前端·javascript·vue.js
Aphasia3117 小时前
react必备JavaScript知识点(二)——类
前端·javascript
玖玖passion7 小时前
数组转树:数据结构中的经典问题
前端
呼Lu噜8 小时前
WPF-遵循MVVM框架创建图表的显示【保姆级】
前端·后端·wpf