首页 / Vue3 入门教程 / 响应式基础(下)

Vue3 入门教程

响应式基础(下)

本教程共 40 篇 · 第 5 篇 · 更新于 2026-07-29 · 约 7 分钟阅读

VueVue3 入门教程深层响应性DOM更新ref解包Proxy

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 —— 旧的断开了
Note

ref 解包只在深层 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) }

countobject 是顶层属性,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,原对象不会被追踪——只用代理版本