[英]Vue 3 reactivity of a primitive ref passed as a prop
While playing with Vue 3 reactivity I incountred a behaviour that I couldn't explain.在玩 Vue 3 反应性时,我遇到了一种我无法解释的行为。
I created a ref
of a primitive.我创建了一个原语的ref
。 Checking isRef
on it returns obviously true
.检查isRef
显然会返回true
。 But when passed to a child component as a prop
, isRef()
and also isReactive()
return false
.但是当作为prop
传递给子组件时, isRef()
和isReactive()
返回false
。 Why?为什么?
Also, even if they both return false
, the watcher of this prop I added in the child component is triggered if the value changes.此外,即使它们都返回false
,如果值发生变化,我在子组件中添加的这个道具的观察者也会被触发。 How could it trigger if the watched value is not a ref
nor reactive
?如果监视的值不是ref
也不是reactive
,它怎么会触发?
Here is a code snippet for both the parent and the child:这是父母和孩子的代码片段:
Parent.vue父视图
let foo = ref(0);
function changeFoo() {
foo.value++;
}
console.log("parent check is reactive?", isReactive(foo)); // retruns false
console.log("parent check is ref?", isRef(foo)); // retruns true
watch(foo, (value, oldValue) => {
console.log("foo changing from", oldValue, "to ", value);
});
Child.vue儿童.vue
const props = defineProps<{
foo: number;
}>();
console.log("child check is reactive?",isReactive(props), isReactive(props.foo)); // returns true false
console.log("child check is ref ?",isRef(props), isRef(props.foo)); // returns false false // Question 1: Why props.foo is not a Ref ?
watch(props, (value, oldValue) => {
console.log("props changing from", oldValue, " to ", value);
});
watch(()=>props.foo, (value, oldValue) => {
// Question 2: Why this Watcher detects changes of "foo" without it being a ref nor reactive ?
console.log("props.foo changing from ", oldValue, " to ", value);
});
And a link to Vue SFC Playground here以及 Vue SFC Playground 的链接
Bonus question: When foo
is passed to a composable used in the child component, the watcher inside the composable is not triggered unless we pass foo
via a toRef but we don't need this additional step if foo
is a ref/reactive object. Why primitives needs this addition step?奖励问题:当foo
传递给子组件中使用的可组合项时,除非我们通过 toRef 传递foo
,否则不会触发可组合项内的观察器,但如果foo
是 ref/reactive object,我们不需要这个额外的步骤。为什么原语需要这个添加步骤吗?
But when passed to a child component as a prop, isRef() and also isReactive() return false.但是当作为 prop 传递给子组件时,isRef() 和 isReactive() 都会返回 false。
You're only telling half the story here, as your own comment actually indicates:正如您自己的评论实际上表明的那样,您在这里只讲了一半的故事:
console.log("child check is reactive?",isReactive(props), isReactive(props.foo)); // returns true false
isReactive(props)
does return true, because the entire props
object (which wraps foo
) is reactive. isReactive(props)
确实返回 true,因为整个props
object(包装foo
)是反应性的。 Any update the parent makes to foo
, gets passed down to the Child as an updated props
object. It's true props.foo
is not a ref/reactive, because it doesn't need to be.父母对foo
所做的任何更新,都会作为更新的props
object 传递给孩子。这是真的props.foo
不是 ref/reactive,因为它不需要。 As long as props
is reactive, props.foo
will update只要props
是反应式的, props.foo
就会更新
The watcher is able to activate on changes to props.foo
because you're actually using special syntax where you pass in a "getter" to the watch specifically meant for watching the property of a reactive object (in your case: props).观察者能够激活对props.foo
的更改,因为您实际上使用特殊语法,您将“吸气剂”传递给手表,专门用于观察反应性 object 的属性(在您的情况下:道具)。 There's an example in the Vue docs that says the same thing. Vue 文档中有一个示例说明了同样的事情。
If you ever needed to assign props.foo
to it's own reactive variable, say to pass to a composable, that's where toRef can be used.如果您需要将props.foo
分配给它自己的反应变量,比如传递给可组合项,那么可以使用 toRef。
// reactive but not synced with props.foo
const newFoo = ref(props.foo)
// reactive AND synced with props.foo
const newFoo = toRef(props, 'foo')
As I indicated with the above comments, if you make a new variable with just ref
, it'll be reactive, but it won't be synced with it's source property, ie the first newFoo
won't reactively update when props.foo
updates, but the second newFoo
will.正如我在上面的评论中指出的那样,如果您只使用ref
创建一个新变量,它将是反应性的,但不会与其源属性同步,即第一个newFoo
不会在props.foo
更新时反应性更新,但第二个newFoo
会。 This is also explained well in the Vue docs这在Vue 文档中也有很好的解释
Simple answer is that in the child, 'props.foo' is reactive when used within the setup/script (ie not in the template according to my playing arounds) but 'foo' isnt reactive if prop is destructured (ie const {foo} = props
).简单的答案是,在孩子中,'props.foo' 在设置/脚本中使用时是反应性的(即根据我的游戏环境不在模板中)但如果 prop 被解构(即const {foo} = props
)。
But you should do this by using the toRef
function ( const foo = toRef(props,'foo')
) to be extra sure OR make the entire props object reactive with toRefs
( const reactiveProp = toRefs(props)
) then refer to it with reactiveProp.foo
(and it will still be reactive even if you destructure it) OR use computed
eg const reactiveFoo = computed(()=>props.foo)
.但是你应该通过使用toRef
function ( const foo = toRef(props,'foo')
) 来做到这一点,或者使整个道具 object 与toRefs
( const reactiveProp = toRefs(props)
) 反应,然后用reactiveProp.foo
(即使你解构它它仍然是反应性的)或使用computed
例如const reactiveFoo = computed(()=>props.foo)
。 You can play around (and see my commented notes here Vue Props Reactivity Playground ).您可以四处游玩(并在此处查看我的评论笔记Vue Props Reactivity Playground )。
Frustrated me for a while cos this is unlike React.js (which I had been using for a long time), the props are reactive (even if destructured).让我沮丧了一段时间,因为这与 React.js(我已经使用了很长时间)不同,道具是反应性的(即使是解构的)。 See test here React Props Reactivity请参阅此处的测试React Props 反应性
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.