响应式基础(下)
本教程共 40 篇 · 第 5 篇 · 更新于 2026-07-29 · 约 7 分钟阅读
5. 响应式基础(下)
本节目标:理解深层响应性、Vue 的 DOM 更新机制、ref 解包的规则和边界情况。
深层响应性
在 Vue 里,状态默认就是深层响应的。也就是说,修改嵌套对象或数组也能被检测到:
import { ref } from 'vue'
const obj = ref({
nested: { count: 0 },
arr: ['foo', 'bar']
})
function mutateDeeply() {
// 这些都能被检测到
obj.value.nested.count++
obj.value.arr.push('baz')
}
ref 可以持有任何值类型,包括深层嵌套的对象、数组、Map 等。当 ref 的值是对象时,内部会调用 reactive() 把它变成响应式代理。
Tip也可以选择不要深层响应性——用
shallowRef()可以只追踪.value的访问。适合优化大对象性能,或状态由外部库管理的场景。
DOM 更新时机
修改响应式状态后,DOM 会自动更新,但不是同步的。Vue 会把更新缓冲到”下一个 tick”,确保每个组件只更新一次,不管你改了多少状态。
要在 DOM 更新完成后执行代码,用 nextTick():
import { nextTick } from 'vue'
async function increment() {
count.value++
await nextTick()
// 此时 DOM 已经更新了
}
打个比方:你连续改了三次数据,Vue 不会手忙脚乱更新三次 DOM。它会攒着,等”下一帧”一起处理,效率更高。
ref 解包规则
作为 reactive 对象的属性
ref 作为 reactive 对象的属性被访问时会自动解包,表现得像普通属性:
const count = ref(0)
const state = reactive({
count
})
console.log(state.count) // 0
state.count = 1
console.log(count.value) // 1
如果把新 ref 赋给已有 ref 的属性,会替换掉旧的:
const otherCount = ref(2)
state.count = otherCount
console.log(state.count) // 2
console.log(count.value) // 1 —— 旧的断开了
Noteref 解包只在深层 reactive 对象内发生。浅层 reactive 对象(
shallowReactive)不会解包。
数组和集合中的 ref
reactive 对象里的 ref 会自动解包,但reactive 数组或 Map 里的 ref 不会:
const books = reactive([ref('Vue 3 Guide')])
// 需要手动 .value
console.log(books[0].value)
const map = reactive(new Map([['count', ref(0)]]))
// 也需要手动 .value
console.log(map.get('count').value)
模板中的解包边界
模板里的 ref 解包有个规则:只有顶层属性才会解包。
const count = ref(0)
const object = { id: ref(1) }
count 和 object 是顶层属性,object.id 不是:
<!-- 正常,count 是顶层属性 -->
{{ count + 1 }}
<!-- 不正常!object.id 不是顶层属性,不会解包 -->
{{ object.id + 1 }}
最后一行会渲染成 [object Object]1,因为 object.id 没被解包,还是 ref 对象。
解决方法——解构成顶层属性:
const { id } = object
{{ id + 1 }} <!-- 现在正常了,渲染 2 -->
Tip文本插值
{{ }}里如果 ref 是最终求值结果,也会自动解包。{{ object.id }}会渲染1。
reactive 的代理机制
reactive() 返回的是原对象的 Proxy,和原对象不相等:
const raw = {}
const proxy = reactive(raw)
console.log(proxy === raw) // false
只有 proxy 是响应式的——改原对象不会触发更新。所以最佳实践是只用代理版本,别留着原对象的引用。
几个一致性保证:
- 同一个对象调用
reactive()总是返回同一个 proxy - 对 proxy 调用
reactive()返回它自己 - 嵌套对象也会被 reactive 包裹
console.log(reactive(raw) === proxy) // true
console.log(reactive(proxy) === proxy) // true
const proxy = reactive({})
proxy.nested = {}
console.log(proxy.nested === {}) // false,嵌套的也是 proxy
本节回顾
- Vue 默认深层响应,嵌套对象/数组的变化也能检测
- DOM 更新是异步的,用
nextTick()等更新完成 - ref 作为 reactive 对象属性自动解包,但在数组/集合里不自动解包
- 模板里只有顶层属性才会解包
reactive()返回 Proxy,原对象不会被追踪——只用代理版本