关于props响应式问题
来源:5-15 Props--v2.6.11(五)

慕粉4283821
2021-01-02
老师,先祝您元旦快乐,新年大吉啦~~
关于props 的响应式,比如 下面的代码
// child 子组件
<template>
<div>{{ a }}</div>
<div>{{ b.c }}</div>
</template>
<script>
props: {
a: {
type: Object
},
b: Object
}
</script>
// parent 父组件
<template>
<child :a="a" :b="b" />
</template>
<script>
data: {
a: { a: 2 },
b: { c: 3 }
}
</script>
首先在父组件 init 时, 会将 a, b 变成响应式对象, 然后在渲染过程中,为什么说 父组件访问了 a 和 b呢? 是因为 给 child 组件传值的时候 访问 了 a b 触发了 getter 吗?
另外在子组件 initProps 时, 也会将 a 和 b 变成响应式对象。 对于a 属性,子组件直接引用了a, 所以 子组件渲染watcher 订阅了 a 这个依赖,而对于 b属性,子组件使用的是 b的c属性, 触发了c属性的getter, 子组件渲染watcher订阅了 c这个依赖。
所以在props 值变化时:
对于属性a :
- 执行 this.a = { a: 3 } 赋值操作,会触发 setter,通知 子组件渲染watcher 更新,所以子组件更新了。
- 执行 thia.a.a = 4 改变对象属性, 同样会触发setter,但此时 通知的是 父组件渲染wathcer, 因为 子组件只遍历了一层。父组件重新渲染了,在 patch 过程,对于组件vnode, 会进行prepatch操作,从而updateChildComponent 改变 对props 属性进行重新计算, 此时 会触发 属性a 的setter, 从而触发 子组件渲染watcher 重新渲染。
对于属性b:
子组件渲染时访问了 b中的c 属性,触发 c属性的getter,子组件渲染watcher 订阅了 依赖。
修改时:
3. 执行 this.b.c = 9 修改对象b中的属性值, 会触发c的setter, 子组件渲染watcher 重新渲染,子组件更新。
4. 执行 this.b = { c: 99 } 赋值操作,此时触发的是 b的setter, 父组件渲染watcher 进行更新,patch 过程 对 props 属性 赋值时, 触发了b的setter, 此时子组件 渲染 wacther 重新渲染,子组件更新。
总结: props的更新 如何影响子组件的重新渲染 和 子组件 视图中如何引用这个props 有关系,这影响到watcher 订阅的依赖 到底是 整个对象,还是某个对象属性。
老师,写的有点多,还请辛苦看下 有没有理解上的误区,感谢老师~
3回答
-
ustbhuangyi
2021-01-04
1. 父组件在模板中把 a,b 传递给子组件,就是访问了 a 和 b,触发了他们的 getter
2. 你的理解有点问题,先看下面截图
所以 执行 this.a = { a: 3 } 是 props 值被修改和你后面的 执行 this.b = { c: 99 } 逻辑是一样的。
thia.a.a = 4 和 this.b.c = 9 是一样的112021-01-05 -
ustbhuangyi
2021-01-05
注意你前一个例子,在父组件定义的数据
data() {
return {
a: {
a: 2
},
b: {
c: 3
}
}
}
当你在父组件修改 this.a.a = 4 的时候,就是触发了 a 的 setter,
你在子组件的模板中引用了 prop a,这个 a 就是 { a: 2} 的对象,它就是指向父组件的 this.a,因为模板中访问了 a,那么自然子组件的 render watcher 也会收集这个 a 依赖
所以当你修改 this.a.a = 4 的时候,就是触发了 a 的 setter,自然就会触发子组件 render watcher 的 update,重新渲染子组件。
我建议你写一个简单的 demo,打上 debugger 断点调试一下更清楚。082021-04-10 -
慕粉4283821
提问者
2021-01-05
我有点不理解: 截图中说的 子组件渲染过程“访问过这个对象prop” 这里的访问是指怎么访问呢?
我个人理解:
// 子组件中的插值 {{ a }} {{ b.c }}
上面的a, b 都是 prop, 我认为 只有 prop中的 a 被访问了,触发 a 的setter, 子组件render watcher会 订阅a的dep。 而 对于对象b,子组件只是访问了对象b的c属性,只会触发c属性的getter,子组件 render watcher 会订阅 c属性的dep。
基于上述理解, 如果直接 对 a 赋值修改,那么会直接触发 子组件重新渲染。
如果对 b 直接赋值进行修改,我认为会触发父组件重新渲染,在父组件重新渲染的patch 过程,对于组件vnode, 执行prepatch,这个过程会修改 子组件的prop属性的值,也就是修改 b的值,但是此时 修改b的值并不能引起子组件重新渲染,因为子组件只是订阅了 c属性的dep。 这里就说不通了。
所以是不是 在组件中写 b.c 也会触发 b的getter呢. 好像是这样的,写了如下例子来证明:
let prop = {}, value = {}; Object.defineProperty(prop, 'b', { get() { console.log('get b'); return value; }, set() { console.log('set prop.b'); return 44; } }) Object.defineProperty(prop.b, 'c', { get() { console.log('get c'); return 2; }, set(newV) { console.log('set prop.b.c'); return 33; } }); prop.b // get b prop.b.c // get b get c 也会触发b的getter, 先取b,再取c prop.b.c = 33; // get b set prop.b.c , 只会触发 c属性的setter
上面的prop 就好比 vue 中的 vm._prop 对象。 所以书写 b.c 的时候 其实是 触发了b的getter, 触发了 子组件render watcher 订阅 b 的dep。 在对 b赋值的时候 就会 触发子组件重新渲染。 而对于修改b的属性,只有修改 b.c 会引起子组件重新渲染。 因为b.c在render时使用了。 修改其他属性 比如 b.x 就不会。
对于a 属性来说,直接修改,引起 子组件render watcher 更新,重新渲染。 如果修改a 的属性值, 也不会触发 a 属性的setter。那视图是怎么更新的呢? 老师在截图里的描述是“对象类型的prop的内部属性变化时候,并没有触发子组件prop的更新,但是在组件渲染过程,访问过这个对象prop,prop触发getter,子组件render watcher 会订阅依赖。然后在父组件更新这个对象prop的某个属性时,会触发setter,从而通知子组件render watcher 重新渲染。”
这里说 组件渲染访问了这个对象prop(也就是a属性),更新内部属性值时,并没有触发a的setter。
比如 a.x = 2 此时只是触发了 x 的setter, a的getter, 并没有触发 a 的setter, 那是怎么通知子组件的呢?
00
相似问题