道具对象的 vuejs3 反应性

vuejs3 reactivity of props object

我对 vuejs 非常狂热,但是组合 API 让我在反应性上有点迷失:

在 .vue 子组件中(由另一个父组件)传递了一个道具,电影:

export default {
    props:{
        movies: Array
    },
    setup(props){}

.

在这个子组件的setup函数体中放置:

A) console.log("props: ",props) // Proxy {}

[above EXPECTED] 上面告诉我 props 对象是一个反应式代理对象。

B) console.log("props.movies: ",props.movies); // Proxy {}

[above UNEXPECTED] 上面告诉我道具键 'movies' 也是一个反应式代理对象,为什么? (因此,为什么不仅主要的 props 对象是反应性的?也许我应该认为这是理所当然的 (例如,将其视为 props = reactive(props) 调用的结果) --> 答案:props 是一个 **deep ** 反应对象(感谢 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 的值也会导致父对象中的 'movies' 反应式对象发生变化,这是为什么呢? 如果我更改 D: let moviesLocal = ref(props.movies); 这甚至成立让 moviesLocal = ref({...props.movies});

非常感谢!期待了解这种行为

使用 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,您需要从 ref 中拆箱 value。无论哪种方式,总有一个 get 操作会触发内部机制并通知 Vue 谁在看什么。

drilling down 在模板和观察者中自动发生,他们知道他们收到了引用,但不会在其他任何地方发生。

https://v3.vuejs.org/guide/reactivity.html#how-vue-tracks-these-changes

理解 Vue 反应性的重要部分是理解 JavaScript - 特别是 Value and Reference 之间的区别。如果您不确定我在说什么,请仔细阅读文章...

在下面的示例中,我将使用 props 对象,因为它是按照以下方式创建的(实际上它并不是 完全 像这样创建的,但运行时行为非常相似):

const props = reactive({ movies: [], price: 100 })
B) console.log("props.movies: ",props.movies); // Proxy {}

above tells me that the props key 'movies' is a reactive proxy object as well, why?

因为当 Vue 创建新的反应对象(通过将现有对象包装到代理中)时,该转换是 deep - 它会影响所有嵌套属性(可以通过使用例如 markRawshallowRef)

就值与引用而言,props 是一个包含对反应式代理对象的引用的变量。该对象有一个 属性 movies ,它包含对数组的反应代理的引用(数组在技术上也是对象)

C) let moviesLocal = props.movies ; 
// ERROR-> getting value from props in root scope of setup will cause the value to loose reactivity

As props.movies is a reactive object, I'd assume that I can assign this to a local variable, and this local variable then also would become reactive. Why is this not the case

变量不是反应性的。只有对象可以是反应性的。在这种情况下,moviesLocal 变量被分配给与 props.movies 相同的反应对象(moviesLocal === props.movies returns true)。此对象仍处于反应状态

让我们在模板中使用 moviesLocal 变量来呈现某种列表...

例如,如果父项改变了 props.movies 引用的数组(使用 [=26= 添加新项],为 props.movies[0] 分配不同的值等),子项将收到有关更改的通知,并且它的模板将重新呈现。

那么为什么会出错呢? “松散反应”在哪里?问题是当父级用 different\new 数组替换 props.movies 的值时会发生什么。我们的 moviesLocal 变量仍将保留对先前反应数组的引用。我们的 子组件仍将对原始数组的更改(突变)做出反应,但失去了对 props.movies 属性.[=45= 的更改做出反应的能力]

这在this demo

中得到了证明

有趣的是,在核心中,这种行为与 Vue 反应性无关。它只是普通的 JS。检查这个:

const obj = {
  movies: ['Matrix']
}
const local = obj.movies
obj.movies = ['Avatar']

此时 local 的值是多少?当然是['Matrix']const local = obj.movies 只是赋值。它不会以某种方式神奇地“link”具有 obj.movies 对象 属性.

值的 local 变量
D) let moviesLocal = ref(props.movies);
moviesLocal.value[0].Title="CHANGED in child component";

I made a local copy in the child component (moviesLocal). This variable I made reactive (ref).

同样,变量 不是反应性的 。它们指向(引用)的对象可以是反应性的。 props.movies 引用的数组已经是反应式代理。您刚刚创建了一个 ref 来保存同一个对象

Changing the value of the reactive moviesLocal also causes the 'movies' reactive object in the parent object to change, why is that ?

因为两者都指向(引用)内存中的同一个数组(由 Vue 代理包装)。为其中之一分配一个新数组,这个“link”将中断...