直接从 vue 3 设置获得的道具不是反应性的
Props gotten directly from vue 3 setup are not reactive
我正在用 vuejs 编写一个应用程序,我想将 prop 传递给子组件,但我收到此错误:
Getting a value from the props
in root scope of setup()
will cause the value to lose reactivity
父组件
<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 };
},
});
如何解决这个问题?
在 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 }
}
}
const trainingCourseId = props.courseId;
它只是说你的 trainingCourseId
没有反应。
我想您发布的代码只是为了演示,因为在这种特定情况下,您实际上可以直接使用 courseId
(在您的模板中),它将是反应式的。
然而,更大的问题仍然存在——为什么 courseId
是反应性的而 trainingCourseId
不是?文档不是说道具是反应对象吗?这里的反应性究竟是如何被破坏的?
明确一点,将 属性 重新分配给局部变量并不总是会删除 所有 反应性(是的,一些反应性总是会丢失,但是根据 属性 的形状,反应性损失最初可能并不那么明显。
Vue 3 使用 Proxy 来实现反应性。这个想法是,对于给定的原始数据对象:{ courseId: "something" }
,Vue 创建另一个 Proxy 对象,它看起来就像给定的数据对象,但所有 属性 getter 和 setter intercepted。反应性来自这些截获的 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 在这种情况下实际上也是一个代理。如果您通过 rxData.courseId.name = "something else"
更改 courseId 名称,更改将反映在 trainingCourseId 中。但是,如果您通过 rxData.courseId = { name: "something else" }
重新分配 rxData.courseId,则此重新分配将对 trainingCourseId 不可见。
另一个答案中提到的 toRef 和 toRefs 方法将帮助您摆脱所有这些有趣的行为。但是,如果您有兴趣,可以查看 关于 vue3 反应性
除了 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)
我正在用 vuejs 编写一个应用程序,我想将 prop 传递给子组件,但我收到此错误:
Getting a value from the
props
in root scope ofsetup()
will cause the value to lose reactivity
父组件
<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 };
},
});
如何解决这个问题?
在 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 }
}
}
const trainingCourseId = props.courseId;
它只是说你的 trainingCourseId
没有反应。
我想您发布的代码只是为了演示,因为在这种特定情况下,您实际上可以直接使用 courseId
(在您的模板中),它将是反应式的。
然而,更大的问题仍然存在——为什么 courseId
是反应性的而 trainingCourseId
不是?文档不是说道具是反应对象吗?这里的反应性究竟是如何被破坏的?
明确一点,将 属性 重新分配给局部变量并不总是会删除 所有 反应性(是的,一些反应性总是会丢失,但是根据 属性 的形状,反应性损失最初可能并不那么明显。
Vue 3 使用 Proxy 来实现反应性。这个想法是,对于给定的原始数据对象:{ courseId: "something" }
,Vue 创建另一个 Proxy 对象,它看起来就像给定的数据对象,但所有 属性 getter 和 setter intercepted。反应性来自这些截获的 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 在这种情况下实际上也是一个代理。如果您通过 rxData.courseId.name = "something else"
更改 courseId 名称,更改将反映在 trainingCourseId 中。但是,如果您通过 rxData.courseId = { name: "something else" }
重新分配 rxData.courseId,则此重新分配将对 trainingCourseId 不可见。
另一个答案中提到的 toRef 和 toRefs 方法将帮助您摆脱所有这些有趣的行为。但是,如果您有兴趣,可以查看
除了 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)