Vue 响应式编程核心揭秘:掌握 ref 与 reactive 的底层逻辑
关键词:Vue3响应式、ref、reactive、Vue Composition API、响应式原理
🔍 一、如何声明响应式状态
使用 [
ref()]
✅ ref() 的三大核心细节:
| 特性 | 描述 |
|---|---|
| 入参 | 接受任意类型的数据(原始值或对象) |
| 返回值 | 返回一个带有 .value 属性的响应式对象 |
| 模板中使用 | 在 <script setup> 中无需返回即可直接使用;在模板中自动解包 .value |
import { ref } from 'vue'
export default {
// `setup` 是一个特殊的钩子,专门用于组合式 API。
setup() {
const count = ref(0)
// 将 ref 暴露给模板
return {
count
}
}
}
💡 小贴士:
- 使用
<script setup>语法时,可以直接在模板中使用ref变量而无需return。 - 支持在事件中直接修改
ref,如count.value++。
🧠 二、为何使用响应式状态 ref
🔄 Vue 响应式工作原理四步走:
- 首次渲染:Vue 会追踪所有被使用的
ref - 数据变化:当
.value被修改时触发 setter => ref 被修改时,它会触发追踪它的组件的一次重新渲染。 - 依赖收集:通过 getter 进行依赖追踪 => 在ref函数返回的对象内部,Vue 在它的 getter 中执行追踪,在它的 setter 中执行触发
- 视图更新:通知组件重新渲染 => 与普通变量不同,你可以将ref 传递给函数,同时保留对最新值和响应式连接的访问。
🧪 看似简单的 ref,背后却有复杂的机制支撑:
// 伪代码,不是真正的实现
const myRef = {
_value: 0,
get value() {
track()
return this._value
},
set value(newValue) {
this._value = newValue
trigger()
}
}
⚡️ 优势对比:
| 普通变量 | ref |
|---|---|
| 无法监听变化 | 自动追踪依赖 |
| 不支持跨函数传递 | 可以安全地传给其他函数 |
| 不具备响应性 | 是 Vue 响应式系统的核心单元 |
📦 三、深层响应式 vs 浅层响应式
🔁 深层响应式(默认行为)
- 对象/数组内部嵌套修改也能被检测到
- 原理:Vue 内部自动调用了
reactive()来包装对象
const user = reactive({
profile: {
name: 'Tom'
}
})
user.profile.name = 'Jerry' // 会被检测到
🌊 浅层响应式(使用 shallowRef / shallowReactive)
- 只追踪顶层属性变化
- 适用于性能敏感场景或大型对象
const shallUser = shallowReactive({
profile: {
name: 'Tom'
}
})
user.profile.name = 'Jerry' // 不会被检测到
🕒 四、DOM更新时机 —— nextTick() 的妙用
Vue 的 DOM 更新是异步进行的,它会在下一个 tick 批量更新所有变更。
- nextTick() => 如果需要等到DOM更新完成只会再执行额外的代码
import { nextTick } from 'vue'
async function increment() {
count.value++
await nextTick()
// 现在 DOM 已经更新了
}
📌 应用场景:
- 获取更新后的 DOM 元素尺寸
- 动态加载内容后需要操作 DOM
- 表单校验反馈等 UI 后续处理
🧩 五、reactive() 详解:让对象原生具有响应性
🔍 ref vs reactive 对比:
| 特性 | ref | reactive |
|---|---|---|
| 数据类型 | 支持任意类型 | 仅支持对象类型(对象,数组和Map,Set等集合类型) |
| 响应方式 | 包装成对象 | 返回 Proxy 代理 |
| 解构问题 | ✔️ 安全解构 | ❌ 解构会丢失响应性 |
| 多次调用 | 每次返回新 ref | 同一个对象返回相同代理 |
🚫 注意事项:
reactive()不支持原始类型(如 number、string),需用ref- 对同一个对象多次调用
reactive()返回的是同一个代理
📄 六、在模板中使用响应式状态的注意事项
⚠️ 解包陷阱:顶级 ref 才能自动解包
const object = { id: ref(1) }
// ❌
{{ object.id + 1 }}
// ✅
const { id } = object
{{ id + 1 }}
// 📝 文本插值始终自动解包:
{{ object.id }} // 1
🎯 总结:响应式开发的黄金法则
| 场景 | 推荐方案 |
|---|---|
| 基础状态管理 | ref() |
| 复杂对象/嵌套结构 | reactive() 或 ref(object) |
| 需要解构使用 | 优先使用 ref() |
| 性能敏感型对象 | 使用 shallowRef / shallowReactive |
| DOM 更新回调 | 使用 nextTick() 控制流程 |