在 Vue 3 中,对普通数据类型(如字符串、数字、布尔值等)实现响应式,其核心方法是使用 ref函数。这是因为 ES6 的 Proxy无法直接代理基本类型值,ref通过一个巧妙的包装机制解决了这个问题。
| 特性 | ref(用于基本数据类型) | 
reactive(用于对象和数组) | 
|---|---|---|
| 包装方式 | 创建一个包含 .value属性的响应式引用对象 | 
直接使用 Proxy代理整个对象 | 
| 数据访问 | 需要通过 .value属性访问和修改内部值 | 
直接访问和修改属性 | 
| 适用场景 | 基本数据类型(string, number, boolean等) | 对象、数组等引用类型 | 
💁 ref的工作原理
ref的实现思路是:如果值本身不能被代理,那就创建一个对象来包装它,然后代理这个对象。
- 
创建包装对象 :当你调用
ref(0)时,Vue 内部实际上会创建一个普通的 JavaScript 对象(通常称为 "引用对象" )来包装你的值:{ value: 0 }。 - 
施加响应式 :接着,Vue 会对这个包装对象使用
reactive方法,即用Proxy将其包裹,使其变为响应式。这样,所有对.value属性的读取和写入操作都能被拦截。 - 
依赖收集与触发更新:
- 
读取 :当你在组件的模板或计算属性中访问
count.value时,Proxy的get拦截器会触发,Vue 会记录下这个依赖关系(这个过程称为 track)。 - 
修改 :当你修改
count.value++时,Proxy的set拦截器会触发,Vue 会通知所有依赖于此值的地方进行更新(这个过程称为 trigger)。 
 - 
 
💡 为什么需要 .value?
你可能会问,为什么不能像操作对象属性那样直接操作 ref变量?关键在于 JavaScript 中基本数据类型是按值传递的 。如果直接传递数字 0,没有任何方法可以拦截到对这个数字本身的修改。而通过 .value访问,实际上是在访问一个响应式对象的属性,这个属性可以被 Proxy精确地拦截和追踪。
不过,Vue 编译器在 模板  中会自动对 ref进行解包,所以你可以在模板中直接使用 {``{ count }}而不需要写 {``{ count.value }}。在 <script setup>中编写的逻辑代码则仍需使用 .value。
🛠️ 使用技巧与最佳实践
- 
模板中自动解包 :在模板里,你可以直接使用
ref变量名,Vue 会自动帮你解包。<template> <button @click="increment">{{ count }}</button> <!-- 无需 .value --> </template> - 
结合
reactive使用 :当你在一个响应式对象内部访问ref时,它也会被自动解包。const count = ref(0); const state = reactive({ count }); console.log(state.count); // 直接输出 0,无需 .value - 
使用
toRefs保持响应式 :当使用reactive定义的对象被解构或展开时,可以使用toRefs将每个属性转换为ref,以保持其响应性。 
💎 总结
简单来说,Vue 3 通过 ref函数将基本数据类型"装箱"成一个对象,然后利用 Proxy代理这个包装对象,从而间接实现了对基本数据类型的响应式追踪。虽然这带来了在 JavaScript 中需要操作 .value的一点额外成本,但模板中的自动解包让开发体验保持了流畅。