本节示例代码如下
父组件
子组件
定位断点
经过之前章节的介绍,我们知道了。父组件中存在components属性时将会调用extend构建子组件构造器,之后在render过程中将会调用createComponent并在该函数中执行extractPropsFromVNodeData提取父组件通过props传递的数据并保存在组件vnode的componentOptions上,并在update的patch过程中执行子组件的init,在组件的init过程中会对props进行两步处理:将props规范成对象形式;对props进行校验并设置响应式最终架设proxy代理。之后当父组件更新时将会调用patchVnode对子组件进行updateChildren并调用子组件的prepatch继而执行updateChildComponent。因此将代码debugger到该函数
change age按钮被点击
当点击change age按钮,将触发子组件更新,执行updateChildComponent函数,传入prop对象即:{age:12,person:{age:12},school:{name:'xxx第一高级中学',address:"xxx市xxx区"}},将代码定位到props的更新位置
toggleObserving用于控制是否对key作响应式处理。由于props已经在父组件的data初始化过程中被Object.defineproperty处理过,且我们本次的触发就是通过修改父组件的props执行过来的,故子组件不需要重复观测
props拿到我们传入的props对象
_propKeys是我们在initProps过程中缓存的props的key数据,它是被组件接收使用的key集合,因此,对于在组件传入未接收的值vue是不做处理的
对所有的key重新校验一遍以合法,并设置props.key。由于props.key已是响应式的了,故会触发set进行派发更新。由于组件在初次渲染时已经对age进行过访问,因此dep中已经收集到了子组件的render watcher。故将会触发子组件watcher的run进行子组件的update将值正确patch到dom上
change person按钮被点击
同样的,在子组件初次渲染时访问过person.age故将子组件render watcher收集到了dep中,因此当触发set时一样能够触发子组件的render watcher做update。不一样的是,person是一个引用值,这意味着,父组件和子组件的person指向的是同一个值,因此当父组件中修改person时将能够直接触发子组件的set进行update(这是因为person.age是先访问后修改的过程,在访问时收集了子组件的dep,由于是一个对象类型故会调用obseve将子属性age观测起来,因此在父组件中调用this.person将收集依赖,this.person.age将触发age的notify)。其效果和子组件中修改是一样的。那么这与vue的单项数据流冲突吗,是否会报warn呢?不会的!因为在init props中传递的setter是这样的
vue只对key设置了set,即{age:12},我们改的是age,而非{age:12},故不会触发warn警告
change school按钮被点击
综合一、二两种,我们可以得出结论,对对象引用的修改会触发新一轮的父组件patch过程,和change age的流程一样。这是因为在决定是否notify之前,会先进行值判断
(两次引用必然不相等)
网友评论