Vue 3 响应式三剑客:ref vs reactive vs Proxy 原理全解析
Vue 3 的发布,带来了 Composition API 和基于Proxy的全新响应式系统。ref和reactive有什么区别?为什么ref要.value?为什么 Vue 3 不再需要Vue.set?今天,我们不看源码,而是从使用到原理,再到手写实现,带你彻底掌握 Vue 3 响应式的核心机制!场景推荐方案基础类型(number, string)ref复杂对象或表单reactive组合式函数返回
为什么
arr[0] = 10
能触发更新?
为什么ref
要.value
?reactive
和ref
到底用哪个?
本文带你从零手搓响应式系统,彻底搞懂 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
?
因为 ++
是复合操作:
get
读取当前值- 计算
+1
set
写回新值
所以 Proxy
的 set
拦截器会被自动触发!
🎯 五、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
🎉 结语:
掌握 ref
、reactive
和 Proxy
,你不仅学会了 Vue 3 的响应式,更理解了现代前端框架的设计哲学。
希望这篇文章能帮你“看透魔法”,成为那个创造魔法的人。
🌱 正在连载《夏天训练营》系列,带你轻松学前端!
💬 欢迎关注,一起成长
更多推荐
所有评论(0)