为什么 arr[0] = 10 能触发更新?
为什么 ref.value
reactiveref 到底用哪个?
本文带你从零手搓响应式系统,彻底搞懂 Vue 3 的魔法内核!


🌟 前言

Vue 3 的发布,带来了 Composition API 和基于 Proxy 的全新响应式系统。
但很多开发者仍在困惑:

  • ref 和 reactive 有什么区别?
  • 为什么 ref 要 .value
  • 为什么 Vue 3 不再需要 Vue.set

今天,我们不看源码,而是从使用到原理,再到手写实现,带你彻底掌握 Vue 3 响应式的核心机制!


🆚 一、ref vs reactive:到底用哪个?

特性 ref reactive
适用类型 任意类型(基础类型、对象) 仅对象(包括数组)
访问方式 count.value count(直接访问)
模板中使用 自动解包,无需 .value 直接使用
解构后是否响应 ❌ 解构后失去响应性 ❌ 解构后失去响应性
底层实现 包装对象 + value 属性 Proxy 代理整个对象

✅ 使用建议:

  • 优先用 ref:基础类型、组合式函数返回值、可能被解构的场景
  • 用 reactive:复杂对象、表单状态、数据结构清晰的对象

💡 小技巧:如果不确定,就用 ref,更灵活!


🤖 二、Vue 3 响应式核心:Proxy 全能代理

Vue 3 放弃了 Object.defineProperty,改用 Proxy,实现了质的飞跃。

🆚 Vue 2 vs Vue 3 响应式对比

能力 Vue 2 (defineProperty) Vue 3 (Proxy)
监听数组索引 arr[0]=x
监听 push/pop ⚠️ 重写方法 ✅ 原生支持
监听新增属性 ❌ 需 Vue.set ✅ 自动监听
性能 初始化遍历所有属性 惰性代理,更高效

Proxy 就像一个“全能管家”,把整个对象包起来,任何读写操作都能拦截!


🛠️ 三、手写 mini reactive:从零实现响应式

我们来实现一个极简版的 reactive,理解其核心机制:

// 模拟视图更新
function update() {
  console.log('%c 视图更新啦!', 'color: red; font-weight: bold;');
}

function reactive(target) {
  if (typeof target !== 'object' || target === null) {
    return target;
  }

  return new Proxy(target, {
    get(obj, key) {
      console.log(`读取属性: ${key}`);
      const res = obj[key];
      return typeof res === 'object' ? reactive(res) : res;
    },
    set(obj, key, value) {
      console.log(`设置属性: ${key} = ${value}`);
      const result = Reflect.set(obj, key, value);
      update(); // 触发视图更新
      return result;
    }
  });
}

测试:验证 Proxy 的强大

const state = reactive({ count: 0, list: [1,2,3] });

state.count++;     // 触发 update
state.list[0]=10;  // 触发 update
state.newProp = 'hi'; // 触发 update

✅ 所有操作均能正确响应!


🔍 四、深入:state.count++ 为什么能触发 set

因为 ++ 是复合操作:

  1. get 读取当前值
  2. 计算 +1
  3. set 写回新值

所以 Proxyset 拦截器会被自动触发!


🎯 五、ref 的本质:一个带锁的盒子

ref 的本质是一个包装对象:

const count = ref(0);

// 实际结构:
// { value: 0 }

它通过 get/set 劫持 value 属性来实现响应式:

class RefImpl {
  constructor(value) {
    this._value = value;
  }

  get value() {
    track(); // 收集依赖
    return this._value;
  }

  set value(newVal) {
    this._value = newVal;
    trigger(); // 触发更新
  }
}

💡 模板中会自动解包 ref,所以不用写 .value


💡 六、最佳实践总结

场景 推荐方案
基础类型(number, string) ref
复杂对象或表单 reactive
组合式函数返回值 ref 或 toRefs
需要解构响应式对象 toRefs(state)
数组操作 reactive(天然支持 push、索引修改等)

📚 参考资料

  • MDN: Proxy
  • Vue 3 源码:packages/reactivity
  • 《Vue.js 设计与实现》——霍春阳

💬 互动环节

🔍 挑战题
如果你在 set 中不使用 Reflect.set,而是直接 obj[key] = value,会发生什么?
(提示:无限递归!)

欢迎在评论区留言你的理解,我们一起讨论成长!


建议标签
#Vue3 #ref #reactive #响应式原理 #Proxy #前端进阶 #CompositionAPI #JavaScript


🎉 结语
掌握 refreactiveProxy,你不仅学会了 Vue 3 的响应式,更理解了现代前端框架的设计哲学
希望这篇文章能帮你“看透魔法”,成为那个创造魔法的人

🌱 正在连载《夏天训练营》系列,带你轻松学前端!  
💬 欢迎关注,一起成长

Logo

葡萄城是专业的软件开发技术和低代码平台提供商,聚焦软件开发技术,以“赋能开发者”为使命,致力于通过表格控件、低代码和BI等各类软件开发工具和服务

更多推荐