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() 控制流程 |