直接从 vue 3 设置中获得的道具不是反应式的

Posted

技术标签:

【中文标题】直接从 vue 3 设置中获得的道具不是反应式的【英文标题】:Props gotten directly from vue 3 setup are not reactive 【发布时间】:2021-12-19 15:52:51 【问题描述】:

我正在使用 vuejs 编写应用程序,我想将 prop 传递给子组件,但出现此错误:

setup() 根范围内的props 获取值将导致该值失去响应性

父组件

<template>
  <div>
      <course-list :courseId = "id"  />
  </div>
</template>
import useRoute from 'vue-router';
import  ref, onMounted, reactive from 'vue';
export default defineComponent(
  components:  NavBar, CourseList, CourseContent ,
  props:
    courseId: String
  ,
  setup()
      const route = useRoute()

      const id = route.params.id
      console.log("the course id is", id);

    return
      id
    
  

子组件

export default defineComponent(
  components:  CourseTopic ,
  props: 
      courseId: 
        type: String
      
    ,
  setup(props) 

    const trainingCourseId = props.courseId;

    return  courses, trainingCourseId ;
  ,
);

我该如何解决这个问题?

【问题讨论】:

这可能是您的解决方案吗? Why is this Vue prop not reacting to change?。如果没有,请分享上面提到的错误消息。 @PeterKrebs 错误消息在问题中,但由于格式不佳而有些隐藏。我已经解决了。 【参考方案1】:

props 上使用toRefs() 以保持道具的反应性:

import  toRefs  from 'vue'

export default 
  setup(props) 
    const  courseId: trainingCourseId  = toRefs(props)
    return  trainingCourseId 
  

toRef():

import  toRef  from 'vue'

export default 
  setup(props) 
    const trainingCourseId = toRef(props, 'courseId')
    return  trainingCourseId 
  

【讨论】:

【参考方案2】:
const trainingCourseId = props.courseId;

它只是说你的trainingCourseId 没有反应。

我猜您发布的代码只是为了演示,因为在这种特定情况下,您实际上可以直接使用courseId(在您的模板中),它将是反应式的。

然而更大的问题仍然存在——为什么courseId 是被动的,而trainingCourseId 不是?文档不是说道具是反应对象吗?这里的反应性究竟是如何被破坏的?

需要明确的是,将属性重新分配给局部变量并不总是会删除所有反应性(是的,一些反应性总是会丢失,但取决于属性的形状,最初的反应性损失可能并不那么明显)。

Vue 3 使用 代理 来实现反应性。这个想法是,对于给定的原始数据对象: courseId: "something" ,Vue 创建另一个代理对象,它看起来就像给定的数据对象,但所有属性 getter 和 setter 被拦截。反应来自这些拦截的 getter 和 setter,因此与拥有 getter 和 setter 的对象相关联,而不是属性本身。

换句话说:

const raw =  courseId: "something" ;
const rxData = reactive(raw);

响应式是 rxData,而不是 courseId,这意味着对 rxData 属性(任何属性,不必是 courseId)的任何访问都是响应式的。但是,当您执行const trainingCourseId = rxData.courseId 时,trainingCourseId 不是代理,它只是一个字符串(从代理中检索)。

当 courseId 不是一个简单的字符串而是一个对象时,这有点不同:

const raw =  courseId:  name: "something"  ;
const rxData = reactive(raw);
const trainingCourseId = rxData.courseId;

在构建反应式代理时,Vue 会递归地转换原始原始数据对象。因此 rxData.courseId 在这种情况下实际上也是一个代理。如果您将 courseId 名称更改为 rxData.courseId.name = "something else",则更改将反映在 trainingCourseId 中。但是,如果您通过 rxData.courseId = name: "something else" 重新分配 rxData.courseId,则此重新分配对 trainingCourseId 不可见。

另一个答案中提到的 toRef 和 toRefs 方法将帮助您摆脱所有这些有趣的行为。但如果你有兴趣,可以查看this question 关于 vue3 反应性

【讨论】:

以上是关于直接从 vue 3 设置中获得的道具不是反应式的的主要内容,如果未能解决你的问题,请参考以下文章

Vue 3 获得风格的道具

Vue3 Composition API 中道具的反应性?

道具无法在 vue 模板中反应

带有 Vue js 道具的反应式 Tailwind 类

作为对象的 Vue 道具的反应性

vue & vuex getter vs 通过道具传递状态