解构丢失响应式,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表现为numberconst { 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) // RefImplconsole.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)
赞助支持
如果这篇文章对你有帮助,欢迎赞助支持!
部分内容可能已过时