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

本节我们的示例如下

app.vue

vue源码解读--组件更新(父组件的更新流程)_第1张图片

child.vue

vue源码解读--组件更新(父组件的更新流程)_第2张图片

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

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

vue源码解读--组件更新(父组件的更新流程)_第3张图片

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

vue源码解读--组件更新(父组件的更新流程)_第4张图片

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

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

vue源码解读--组件更新(父组件的更新流程)_第5张图片

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

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

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

vue源码解读--组件更新(父组件的更新流程)_第6张图片

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

vue源码解读--组件更新(父组件的更新流程)_第7张图片
(从获取的真实dom元素el调用的api可以看出,vue是在一定的条件下新增或删除一些属性)

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

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

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

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

vue源码解读--组件更新(父组件的更新流程)_第8张图片

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

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

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

vue源码解读--组件更新(父组件的更新流程)_第9张图片

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

                        step1

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

vue源码解读--组件更新(父组件的更新流程)_第10张图片

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

vue源码解读--组件更新(父组件的更新流程)_第11张图片

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

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

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

你可能感兴趣的:(vue源码解读--组件更新(父组件的更新流程))