直接从 vue 3 设置获取的 props 不是响应式的

etr*_*anz 27 vue.js reactive vue-props vuejs3

我正在 vuejs 中编写一个应用程序,我想将 prop 传递给子组件,但我收到此错误:

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

父组件

<template>
  <div>
      <course-list :courseId = "id"  />
  </div>
</template>
Run Code Online (Sandbox Code Playgroud)
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
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

子组件

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

    const trainingCourseId = props.courseId;

    return { courses, trainingCourseId };
  },
});
Run Code Online (Sandbox Code Playgroud)

我该如何解决这个问题?

ton*_*y19 54

使用toRefs()onprops来保持道具的反应性:

import { toRefs } from 'vue'

export default {
  setup(props) {
    const { courseId: trainingCourseId } = toRefs(props)
    return { trainingCourseId }
  }
}
Run Code Online (Sandbox Code Playgroud)

或者toRef()

import { toRef } from 'vue'

export default {
  setup(props) {
    const trainingCourseId = toRef(props, 'courseId')
    return { trainingCourseId }
  }
}
Run Code Online (Sandbox Code Playgroud)


Xin*_*hao 22

const trainingCourseId = props.courseId;
Run Code Online (Sandbox Code Playgroud)

它所说的只是你的trainingCourseId不是反应性的,这意味着如果父组件传递不同的值courseId,你trainingCourseId不会有新的值(非常清楚,props.courseId将被更新,这只是你trainingCourseId没有更新)。

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

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

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

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

换句话说:

const raw = { courseId: "something" };
const rxData = reactive(raw);
Run Code Online (Sandbox Code Playgroud)

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

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

const raw = { courseId: { name: "something" } };
const rxData = reactive(raw);
const trainingCourseId = rxData.courseId;
Run Code Online (Sandbox Code Playgroud)

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

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


egi*_*ocs 11

除了 toRef() 和 toRefs() 之外,我们还可以计算 props 以使其具有反应性。

使用打字稿设置的示例:

import { computed } from 'vue'

const props = defineProps({
  prop1: {
    type: String
  },
  prop2: {
   type: Boolean,
   default: () => false
  }
})

const prop1 = computed(() => props.prop1)
const prop2 = computed(() => props.prop2)
Run Code Online (Sandbox Code Playgroud)