为什么在Vue3 setup()中直接解构props会丢失响应性详解
很多使用 Vue3 的朋友都遇到过一个问题:在 setup() 函数里直接解构 props,数据就不再响应了。这是为什么呢?
我们先看一个例子。假设我们有一个组件,它接收一个 user 属性。在 setup() 里我们可能会这样写:
export default {
props: ['user'],
setup(props) {
const { name } = props.user
return { name }
}
}
这样写看起来没问题,但实际上,name 已经失去了响应性。父组件更新 user 时,这里的 name 不会跟着变。
原因:理解 Vue3 的响应式原理
要明白原因,得先知道 Vue3 的响应式原理。Vue3 用 Proxy 来追踪数据变化。props 本身是响应式的,但当你解构出 props.user.name 时,你拿到的是一个普通的值。这个值和原来的响应式数据断开了联系。
一个比喻
就像你有一根水管,水在水管里流动。你把水接出来放到杯子里,水还在杯子里。但水管里再流动的水,就和杯子里的水没关系了。
Vue3 的响应式系统也是这样工作的。它只能追踪那些被 reactive 或 ref 包裹的数据。直接解构出来的值,只是一个普通的 JavaScript 值,系统不知道这个值需要被追踪。
正确的做法是什么?
那么,正确的做法是什么呢?有两种方法。
方法一:避免提前解构
不要提前解构,在模板里直接使用 props.user.name。这样就能保持响应性。
方法二:使用toRefs
import { toRefs } from 'vue'
export default {
props: ['user'],
setup(props) {
const { user } = toRefs(props)
const name = user.value.name
return { name }
}
}
toRefs 会把响应式对象的每个属性都转成 ref。这样解构出来的属性还是响应式的。
注意细节
toRefs处理的是props本身,不是props.user。因为props才是响应式对象,props.user可能只是一个普通对象。
如果你需要解构 props.user 里的属性,可以这样写:
import { toRefs, reactive } from 'vue'
export default {
props: ['user'],
setup(props) {
const user = reactive({ ...props.user })
const { name } = toRefs(user)
return { name }
}
}
先用 reactive 包裹,再用 toRefs 解构。这样 name 就是响应式的了。
特殊情况:将props值传递给函数
还有一种情况要注意。有些时候,你解构 props 是为了传值给其他函数。比如:
setup(props) {
const { id } = props
fetchData(id)
}
这样写,id 变化时,fetchData 不会重新执行,因为这里的 id 只是一个普通值。
正确的写法是用 watch 或 watchEffect:
import { watch } from 'vue'
setup(props) {
watch(
() => props.id,
(newId) => {
fetchData(newId)
}
)
}
这样,props.id 变化时,fetchData 就会用新的 id 重新执行。
总结
直接解构 props 会丢失响应性,因为解构出来的是普通值。要保持响应性,可以:
- 在模板里直接使用
props.xxx - 用
toRefs处理后再解构 - 需要响应式地使用
props值时,记得用watch或watchEffect
相关文章
解决element-ui中Popconfirm气泡确认框的事件不生效问题
这篇文章主要介绍了解决element-ui中Popconfirm气泡确认框的事件不生效问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-07-07
vue3结合ts从零实现vueuse的useRouteQuery方法
这篇文章主要为大家详细介绍了如何使用vue3与ts从零实现一个类vueuse的useRouteQuery方法,并解决vueuse的useRouteQuery方法存在的一些问题,感兴趣的可以了解下2024-03-03


最新评论