为何在 Vue3 setup() 中直接解构 props 会丢失响应性?

张开发
2026/4/18 21:20:16 15 分钟阅读

分享文章

为何在 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。这样就能保持响应性。方法二使用toRefsimport { 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或watchEffectimport { watch } from vue setup(props) { watch( () props.id, (newId) { fetchData(newId) } ) }这样props.id变化时fetchData就会用新的id重新执行。总结直接解构props会丢失响应性因为解构出来的是普通值。要保持响应性可以在模板里直接使用props.xxx用toRefs处理后再解构需要响应式地使用props值时记得用watch或watchEffect

更多文章