Vue3避坑指南:为什么解构props会丢失响应性,以及如何正确处理

更新日期: 2026-04-02 阅读: 15 标签: 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,这样就能保持响应性。

<template>
  <div>{{ props.user.name }}</div>
</template>

<script setup>
const props = defineProps(['user'])
</script>

方法二:使用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)
    }
  )
}

或者使用watchEffect:

import { watchEffect } from 'vue'

setup(props) {
  watchEffect(() => {
    fetchData(props.id)
  })
}

这样,props.id变化时,fetchData就会用新的id重新执行。


使用script setup时的注意事项

如果你使用的是<script setup>语法糖,情况会稍有不同:

<script setup>
const props = defineProps(['user'])

// ❌ 错误:解构会丢失响应性
const { name } = props.user

// ✅ 正确:直接使用props
// 在模板中直接使用 props.user.name

// ✅ 正确:使用toRefs
const { user } = toRefs(props)
const name = computed(() => user.value.name)
</script>


总结

直接解构props会丢失响应性,因为解构出来的是普通值。要保持响应性,可以:

  • 在模板里直接使用props.xxx

  • 用toRefs处理后再解构

  • 需要响应式地使用props值时,记得用watch或watchEffect

记住这个核心原则:只有通过响应式API(ref、reactive、toRefs等)包裹的数据,Vue的响应式系统才能追踪到它们的变化。

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/13557

相关推荐

props,挂载函数和快照测试

我们测试了通过一些 props 的结果。但是实际上,我们可以直接测试 props。让我们回到上次的 ToDoList组件,不过这次要用一个新的 Task 组件。我们将要测试 ToDoList 组件是否渲染 Task 组件,并将任务名称传递给他们。

TypeScript中的React Render Props

和之前的文章一样,本文也要求你对render props有一些知识背景,如果没有官方文档可能会对你有很大的帮助。本文将会使用函数作为children的render props模式以及结合React的context API来作为例子。

props、属性、事件传递

之前看了一篇关于Vue开发技巧的文章,其中提到了在写高级组件时,通过v-bind=$props将props一次性向下传递。这种向下传递的方式我之前没有用过,便看了下官网的介绍,并补充了一些相关API用法,在这里记录一下,方便自己以后查看

Node.js之react.js组件-Props应用

render props是指一种在 React 组件之间使用一个值为函数的 prop 共享代码(个人理解:将组件进行函数化,通过调用组件名实现,组件的利用,以元素的形式调用,并渲染画面),具有 render prop 的组件接受一个函数

React组件设计模式-Render-props

写业务时,我们经常需要抽象一些使用频率较高的逻辑,但是除了高阶组件可以抽象逻辑,RenderProps也是一种比较好的方法。RenderProps,顾名思义就是将组件的props渲染出来。实际上是让组件的props接收函数,由函数来渲染内容。将通用的逻辑

Vue 中的 Props 与 Data 细微差别,你知道吗?

Vue提供了两种不同的存储变量:props和data。这些方法一开始可能会让人感到困惑,因为它们做的事情很相似,而且也不清楚什何时使用props,何时使用data。那么props和data有什么区别呢?

验证 Vue Props 类型,这几种方式你可能还没试用过!

vue 要求任何传递给组件的数据,都要声明为 props。此外,它还提供了一个强大的内置机制来验证这些数据。这就像组件和消费者之间的契约一样,确保组件按预期使用。

vue props传值常见问题

传入的值想作为局部变量来使用,直接使用会 报错。错误是说的避免直接修改父组件传入的值,因为会改变父组件的值,解决方案:可以在data中重新定义一个变量,改变指向,但是也只是针对简单数据类型

React组件的state和props

React 的数据是自顶向下单向流动的,即从父组件到子组件中,组件的数据存储在 props 和 state 中。实际上在任何应用中,数据都是必不可少的,我们需要直接的改变页面上一块的区域来使得视图的刷新

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!