vue源码解读--组件更新(父组件的更新流程)

本节我们的示例如下

\bullet app.vue

\bullet child.vue

当点击按钮,切换flag的值,此时将触发get收集依赖并触发set向dep分别notify,这将在下一个tick中触发更新,执行patch,接收新旧两个vnode

上一节,我们分析过,组件更新的分界点为sameVnode

    \star 当前组件未定义key、tag为main、非注释节点、都有data定义。故return true。调用patchVnode,入参为:旧的vnode、新的vnode、[]

    \star 用红色框框圈出的位置,是针对组件而言。vue在render过程中遇到子组件会调用createComponent方法,该方法向组件data上挂载了hook(init、prepatch、insert、destory);postpatch和update则是mergeVNodeHook时挂载的。在本次patch流程中的根vnode是app.vue的,故先跳过(step:1)不看

    \star 每次render都会生成一个vnode,其children则保留着根节点的html元素对应的vnode。故oldCh和ch拿到的都是组件vnode、空文本节点vnode、button按钮对应的vnode。调用isPatchable

            \circ 根据之前分析得知,vnode.componentInstance是在组件render过程中调用data.hook.init时缓存的,换言之,只有组件的vnode有componentInstance值,那么同样的,当前的patch流程不是组件child,故不会进入while循环,返回true

        \star 返回patchVnode,调用cbs.update。该方法是在createPatchFunction过程中保存的hooks键('create', 'activate', 'update', 'remove', 'destroy'),对应的值为createPatchFunction传入的modules,即各个模块对应的创建或更新的钩子函数,如updateAttrs

                \circ 进入updateAttrs函数,对在标签上定义的属性值进行比对更新

                \ominus 对attrs(id="app")进行更新,我们这里两次的id值是相同的,假设id的值发生了变化,则调用setAttr

(从获取的真实dom元素el调用的api可以看出,vue是在一定的条件下新增或删除一些属性)

\star 因此得出结论,cbs.update实际上就是针对dom的各个模块调用原生的一些dom方法进行比对更新\star               

        \star 返回patchVnode,调用hook上的update针对组件做一些更新,同样的,由于当前patch流程非组件vnode,故跳过

         \star 代码向下,判断vnode.text是否存在。如果该值存在,则说明该组件只有一个文本节点,因此只需要调用原生dom api设置根元素的textContent进行简单的文本更新即可

          \star 我们当前并不满足文本节点条件,故进入if判断,由于oldCh 和 ch来源自不同的引用,故条件为true,调用updateChildren方法,入参为:id为app的div、新旧vnode、[]、undefine。也就是说,在此之前,vue针对的是template内的根元素做的更新,其子元素还未有任何操作   

            \star oldStartIdx=0、oldEndIdx=2、newStartIdx=0、newEndIdx=0、oldStartVnode=组件vnode、oldEndVnode=button按钮、newStartVnode=组件vnode、newEndVnode=button按钮

            \star 调用checkDuplicateKeys,判断key是否重复,因为典型的ul+li结构下vue是要求我们给定不同的key的,因为vue在更新阶段会根据key进行更新走不同的逻辑,因为这里是在确保key是唯一的

            \star   执行while循环,这实际上就是我们常提起的高大上的diff算法的核心

                    \circ 符合判断,进入while循环。走到sameVnode判断中,再次调用patchVnode,入参为:新旧组件vnode、【】、包含三个子元素的组件vnode、0,重复之前的逻辑,在本次的patch流程中将进入之前跳过的step

                        \odot step1

                            --调用prepatch,入参为新旧组件vnode

                            --调用updateChildComponent,入参为上一次(点击按钮前组件render过程中生成的vnode,是实际的渲染vnode)、props(flag:true)、定义的事件、组件vnode、undefined。在该函数中将对一些值进行访问和更新,以触发其set触发notify更新,因此将执行子组件的patch过程

            \star 回到while循环,这次拿到的是中间的文本节点,再次进入patchvnode,此次将执行到nodeOps.setTextContent(elm, vnode.text)更新text文本节点

            \star 再次回到while循环,本次拿到的是button按钮,由于button没有改变,因此实际上是没有被更新的

    当三次更新完毕后,则app.vue更新流程结束

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容