简体   繁体   English

作为道具传递的原始 ref 的 Vue 3 反应性

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM