942 字
5 分钟

解构丢失响应式,ref何时自动解包

欢迎来到我的博客!#

使用 Markdown 格式编写。

今天学到了什么#

  • 解构丢失响应式
  • ref何时自动解包

解构丢失响应式#

关键点在于:你以为 counterStore.count 还是那个 ref 对象,但在 Pinia(以及 Vue 的 reactive)里它通常已经被“自动解包(unref)”成普通值了。所以你解构拿到的是“值”,不是“ref 的地址”。

分两种情况看就很清楚:

1)普通对象里放 ref:解构不会丢

import { ref } from 'vue'
const counterStore = { count: ref(0) }
const { count } = counterStore
count.value++ // 会更新
console.log(counterStore.count.value) // 1

这里 counterStore.count 就是 ref 对象本身,解构出来的 count 仍然指向同一个 ref(你说的“地址”没问题)。

2)reactive/Pinia store:属性访问会自动解包,解构就变成“值快照”

import { reactive, ref } from 'vue'
const store = reactive({ count: ref(0) })
store.count // 注意:这里拿到的是 0,不是 ref 对象(被解包了)
const { count } = store
// count 此时是 0(普通 number),当然不会响应式更新

Pinia 的 store 本质上也是一个带 Proxy 的响应式对象,并且会把 state(尤其是 setup store 里返回的 ref)store.xxx 这种访问时自动解包,让你用起来像普通属性。于是:

  • counterStore.count 表现为 number
  • const { count } = counterStore 拿到的就是当下的 number,后续不再跟 store 的 getter/setter 绑定

所以你问的这句:

“假设 counterStore 里面的 count 是 ref,解构后的 count 不就是地址吗?”

Pinia 的使用视角里,前半句往往不成立:“里面是 ref”不等于“你从 counterStore.count 读到的是 ref”,因为它被解包了。

正确姿势(Pinia):用 storeToRefs 把每个属性重新变成 ref 再解构

import { storeToRefs } from 'pinia'
const counterStore = useCounterStore()
const { count } = storeToRefs(counterStore) // count 是 ref,响应式保留

ref何时自动解包#

ref 的“自动解包(unref)”不是随时发生的,它只在特定上下文里发生。你可以按这几条规则记:

1)在模板里:几乎总会自动解包

  • <template> 中使用 ref,写 count 等价于 count.value
  • 这是最常见、最稳定的一条。

2)在响应式代理对象里(reactive() / Pinia store 这类 Proxy):读取属性会自动解包

const state = reactive({ count: ref(0) })
state.count // -> 0(自动解包)
state.count = 2 // -> 等价于 state.countRef.value = 2(自动“回写”到 ref)

要点:“通过 Proxy 属性访问”触发解包。所以你写 store.count 往往拿到的是值,不是 ref 本体。

3)在脚本里:顶层 ref 不会自动解包

const count = ref(0)
console.log(count) // RefImpl
console.log(count.value) // 0

也就是说:在 <script setup>/JS 代码中,count 仍然是 ref,你必须 .value(除非它被放进了 reactive/Pinia 这种会解包的代理里,见第 2 条)。

4)解构时:不会“保持绑定”,通常就是你丢响应式的根源

  • reactive() / Pinia store 做 const { count } = store
    • 因为取属性那一刻已经解包成值了,所以解构得到的是普通值快照。
  • 正解:toRefs(state) / storeToRefs(store) 先把属性变回 ref 再解构。

5)例外/边界(容易踩坑)

  • reactive() 对象里如果属性本身就是一个“对象 ref”,读取会解包到那个对象,但深层行为仍要看你怎么用。
  • 数组/Map/Set 等容器里存 ref,一般不会像对象属性那样有同样的解包体验(别指望“到处都自动 .value”)。

一句话总结

  • 模板:自动解包
  • reactive()/store 的“属性访问”:自动解包
  • 在普通对象中:如果 ref 是一个普通 JavaScript 对象的属性,它不会被解包。
const obj = { count: ref(0) }
console.log(obj.count.value) // 需要 .value
  • 解构:不会帮你保留响应式引用(用 toRefs/storeToRefs

赞助支持

如果这篇文章对你有帮助,欢迎赞助支持!

赞助
解构丢失响应式,ref何时自动解包
https://firefly.cuteleaf.cn/posts/study-everyday/2026-05-11今日学习-解构丢失响应式ref何时自动解包/
作者
LJC
发布于
2026-05-11
许可协议
CC BY-NC-SA 4.0
最后更新于 2026-05-11,距今已过 113 天

部分内容可能已过时

目录