对于 非根组件的检测
来源:4-2 响应式对象(上)

fy000
2019-12-11
老师请问代码中写道:
if (!isRoot) {
toggleObserving(false)
}
按代码意思就是子组件的props就不需要observe了 , 那么有一个问题 ,如果是父->子->子 三级嵌套呢
就比如:
list 父组件
detail:子组件 (props:[“id”],接收来自list组件的id),
title: 孙子组件(props:[“text”],接收 detail的 text文本),
那么判断detail的时候 vm.$parent是存在的(list),那就不对他进行observe了?
还有一个问题:
就是无论shouldObserve与否 最后都会调用defineReactive, class Observer 这个类我看只是处理了数组和对象的观察形式啊 并没有其他操作啊,是对象那还是直接调用defineReactive, 那么shouldObserve不就没啥意思了么?
对于第二个问题 L后来理解为 shouldObserve为false 只Object.defineProperty当前这一层(defineReactive(props, key, value)),如果为true 则 递归每一层都观察?
1回答
-
ustbhuangyi
2019-12-11
对于非根组件的 prop 来说,它的值是由父组件传入而来,初始值的计算在 initProps 函数中
通过 validateProp 函数计算而来,并且通过 defineReactive 把 prop 的值变成响应式。那么子组件的 prop 值是怎么更新的呢?
在父组件更新过程中,会有个 prepatch 的过程,会执行到 lifecycle.js 中的 updateChildComponent 函数,其中有一段代码是对 props 做重新计算。
可以看到这里是直接对 props[key] 赋值,因此会触发 setter,如果计算出来的值发生变化,则进而触发子组件的重新渲染。那么为何加一个 shouldObserve 的判断呢,这个 shouldObserve 是在 observe 函数中判断的:
正如你说的,props 在 defineReactive 会递归执行 observe,那么由于 shouldObserve 为 false,所以相当于只对最外层做了一层 defineReactive,那为什么只对最外层做一层 defineReactive 就够了呢。我们来举个例子。
考虑 props 是对象的情况,组件 B 有一个对象类型的 prop a,组件 A 定义了 data a,现在点击按钮,更新 a 中的属性 b,组件 B 会重新渲染吗?
答案是会,但是重新渲染的原因是什么呢?是我们上面提到的在 updateChildCompent 过程中,重新计算子组件的 prop 值触发的重新渲染吗?不是的,因为这里的 prop 是一个对象,它始终指向对象 a,这个引用没有变化,在 setter 中求值发现结果一样,并不会触发重新渲染。
那么为何组件 B 能重新渲染呢?因为在组件 A 的初始化过程中,会执行 initData,进而 observe(data)
observe 实际上就是一个递归执行 defineReactive 的过程,所以对于 this.a.b 也定义了 getter 和 setter。接着在首次渲染 B 组件过程中,即执行 B 组件的 render 函数中,访问到了 A 组件的 a.b(因为指向的是同一个引用,也就是 A 组件中的 a 对象),触发了 getter 做了依赖收集,把 B 组件的 render watcher 收集起来,因此当我们在 A 组件中对 a.b 修改的时候,就触发了 setter,这个值由 1 变成 2,改变了因此会触发所有的依赖更新,也就触发了 B 组件的 render watcher 的 update,所以就触发了 B 组件的重新渲染。
所以结论就是对于 prop,只需要做一层的 defineReactive 即可,这就是 shouldObserve 的用处。232019-12-12
相似问题