https://cn.vuejs.org/guide/essentials/reactivity-fundamentals...
https://play.vuejs.org/#eNp9UcFuwjAM/ZUoF4qoShE3VJC2iQM7bNO2Y...
如代码所示,解构丢失响应性连接似乎并不只是reactive的“局限性”
对,你提到的这个问题不仅限于reactive()
,包括ref()
在内的一些Vue3的反应性API在处理解构时都有一定的问题。
Vue3 的反应性系统在处理解构操作时,无法跟踪解构的子属性变化。例如,如果你有一个对象,并且使用reactive()
对其进行了反应性处理,然后通过解构来访问其属性,那么当这些属性发生改变时,Vue3的反应性系统无法跟踪这些变化。
以下是一个例子:
import { reactive } from 'vue'
const state = reactive({
obj: {
value: 1,
},
})
const otherState = { ...state.obj } // 解构操作
在这个例子中,otherState
是从 state.obj
中解构出来的,然而它并不会保持与 state.obj
的反应性连接。如果 state.obj.value
改变,otherState.value
不会更新。
这个问题的根源在于 Vue3 的反应性系统只能跟踪通过反应性对象直接导出的属性,而无法跟踪通过解构得到的属性。对于这种情况,你需要手动使用 ref()
来创建反应性引用。例如:
import { reactive, ref } from 'vue'
const state = reactive({
obj: {
value: ref(1), // 使用ref来创建反应性引用
},
})
const otherState = { ...state.obj } // 解构操作
在这个例子中,即使 state.obj.value
改变,otherState.value
也会更新,因为 state.obj.value
是一个反应性引用。
reactive()
的话可用利用 toRefs()
方法转换为 ref 变量构成的对象从而使用结构赋值,参见 https://vuejs.org/api/reactivity-utilities.html#torefs 以及官方的示例
const state = reactive({
foo: 1,
bar: 2
})
const { foo, bar } = toRefs(state)
// The ref and the original property is "linked"
state.foo++
console.log(foo.value) // 2
foo.value++
console.log(state.foo) // 3
6 回答2.9k 阅读✓ 已解决
8 回答4.6k 阅读✓ 已解决
6 回答3.3k 阅读✓ 已解决
6 回答2.3k 阅读
5 回答6.3k 阅读✓ 已解决
3 回答2.4k 阅读✓ 已解决
3 回答2.1k 阅读✓ 已解决
因为截至目前, JS 无法监听变量的赋值行为,不管是
Object.defineProperties
还是Proxy
,其实都是拦截对象的属性变化,而不是监听变量的赋值。其实也正是因为变量赋值无法被监听,Vue3 才搞出
ref
这个东西来,把基础类型包装进一个对象里,就可以对这个对象的属性.value
进行监听了。不过 JS 不能监听,不代表无法实现这样的语义,有一个比较年轻的 Svelte 框架,通过预编译,对用户屏蔽类似的包装过程,所以如果是使用 Svelte 的话,延时器回调中对变量赋值,可以直接反映到视图上:
但是 Vue 目前不允许这样写。
其实 Vue 有过一个提案,用 JS 的 label 语句来代替
ref
函数:但是由于这个提案给 label 语句带来了歧义,在 Vue 社区和开发者群体中引起了强烈的反对,因此只得作罢,所以时至今日, Vue 的响应式还是以监听对象属性的方式来完成,对解构而来的变量重新赋值不会被监听。