我对vuejs非常着迷,但是复合API使我在反应性方面有点迷失了:
在.vue子组件中,将传递一个道具(由另一个父组件传递),即电影:
export default {
props:{
movies: Array
},
setup(props){}。
放置在此子组件的安装函数主体中:
A) console.log("props: ",props) // Proxy {}上面的预期告诉我,props对象是一个反应性代理对象。
B) console.log("props.movies: ",props.movies); // Proxy {}上面出乎意料的上面告诉我,道具键“电影”也是一个反应性的代理对象,为什么?(因此,为什么主道具对象不仅是反应性的?也许我应该认为这是理所当然的(例如,把它想象成支持=反应性( props )调用的结果) -->答案:道具是一个**深的**反应性对象(谢谢Michal Lev!)
C) let moviesLocal = props.movies ; // ERROR-> getting value from props in root scope of setup will cause the value to loose reactivity由于props.movies是一个反应性对象,因此在上述意外情况下,我假设可以将其赋值给局部变量,然后这个局部变量也将成为反应性变量。为什么不是这样(我得到上面显示的错误)
D) let moviesLocal = ref(props.movies);
moviesLocal.value[0].Title="CHANGED in child component";上面意外地,我在子组件(moviesLocal)中创建了一个本地副本。这个变量我做了反应(参考)。更改反应性moviesLocal的值也会导致父对象中的“电影”反应性对象发生变化,为什么呢?如果我更改D:设moviesLocal = ref(props.movies);让moviesLocal =ref({...props.movies}),则这甚至成立;
非常感谢!期待着了解这种行为
发布于 2022-01-08 15:50:19
理解Vue反应性的重要部分是理解JavaScript --特别是价值和参考之间的差异。如果你不知道我在说什么,仔细阅读这篇文章.
在下面的示例中,我将使用props对象,因为它是按照以下方式创建的(实际上,它不是这样创建的,但运行时行为非常类似):
const props = reactive({ movies: [], price: 100 })B) console.log("props.movies: ",props.movies); // Proxy {}上面告诉我道具键“电影”也是一个反应性的代理对象,为什么?
因为当Vue创建新的反应性对象(通过将现有对象包装到代理中)时,该转换是深度 --它影响所有嵌套属性(可以通过使用markRaw或shallowRef等方法更改此行为)。
就值与引用而言,props是一个包含对反应性代理对象的引用的变量。该对象具有一个属性movies,该属性包含对数组的反应性代理的引用(数组在技术上也是对象)。
C) let moviesLocal = props.movies ;
// ERROR-> getting value from props in root scope of setup will cause the value to loose reactivity由于props.movies是一个反应性对象,我假设我可以将它赋值给一个局部变量,然后这个局部变量也会变成反应性的。为什么不是这样呢?
变量不是反应性的。只有对象可以是反应性的。在这种情况下,moviesLocal变量与props.movies (moviesLocal === props.movies返回true)引用相同的反应性对象进行赋值。这个对象是,仍然是反应性。
让我们使用模板中的moviesLocal变量来呈现某种列表.
例如,如果父变量改变了props.movies引用的数组(使用push()添加新项,为props.movies[0]分配不同的值等)子程序将被通知更改,其模板将重新呈现。
那么为什么要出错呢?“松散反应性”在哪里?问题是当父代用不同的\新数组替换props.movies的值时会发生什么。我们的moviesLocal变量仍将保存对以前的反应性数组的引用。我们的props.movies子组件仍将对原始数组的更改(突变)作出反应,但失去了对属性的更改作出反应的能力。
这在这个演示中得到了演示。
有趣的是,在核心部分,这种行为与Vue反应性无关。这只是简单的JS。检查一下这个:
const obj = {
movies: ['Matrix']
}
const local = obj.movies
obj.movies = ['Avatar']此时local的价值是什么?当然是['Matrix']!const local = obj.movies只是价值分配。它不会神奇地将local变量与obj.movies对象属性值“链接”。
D) let moviesLocal = ref(props.movies);
moviesLocal.value[0].Title="CHANGED in child component";我在子组件(moviesLocal)中创建了一个本地副本。这个变量我做了反应(参考)。
同样,变量不是反应性。它们指向的对象(引用)可以是反应性的。props.movies引用的数组已经是反应性代理。您刚刚创建了一个包含相同对象的ref。
更改反应性moviesLocal的值也会导致父对象中的“电影”反应性对象发生变化,为什么呢?
因为两者都指向内存中(引用)相同的数组(由Vue代理包装)。给他们中的一个分配一个新数组,这个“链接”就会中断.
发布于 2022-01-08 11:12:50
使用toRef来维护反应性。
const moviesLocalRef = toRef(props, 'movies')请参阅https://v3.vuejs.org/api/refs-api.html#toref
规则是,必须始终从对象的根访问reactive对象子属性。a.b.c.d。您不能只使用break off a piece and modify it,因为从根解析的操作允许Vue跟踪更改。
在您的示例中,内部属性是相同的,但是Vue已经失去了跟踪更改的能力。
另外,您还可以创建一个计算引用。
const moviesLocalRef = computed(()=>props.movies)无论您使用computed ref还是toRef,都需要在模板之外使用theRef.value访问属性。正如我在总结中指出的那样,这就是允许Vue保持反应性的原因。
结论
下面是我选择如何考虑Vue3反应性的方法。
来自对象的dereferencing a property行为是触发魔法的原因。
对于一个reactive object,您需要从reactive object开始,然后对您感兴趣的属性进行drill down。对于ref,需要将value从ref中解压缩。无论哪种方式,总是有一个get操作触发内部机制并通知正在监视什么的Vue。
这个drilling down自动发生在模板和观察者中,他们知道他们收到了一个引用,但是没有其他地方。
请参阅https://v3.vuejs.org/guide/reactivity.html#how-vue-tracks-these-changes
https://stackoverflow.com/questions/70631273
复制相似问题