道具对象的 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 - 它会影响所有嵌套属性(可以通过使用例如 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
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= 的更改做出反应的能力]
中得到了证明
有趣的是,在核心中,这种行为与 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”将中断...
我对 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 - 它会影响所有嵌套属性(可以通过使用例如 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
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= 的更改做出反应的能力]
有趣的是,在核心中,这种行为与 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”将中断...