然后,VueComponent实例的渲染Watcher再调用updateComponent函数重新render:
在$options中取到render函数执行:
下面这个keepalive组件的render函数对于理解keepalive非常重要,在keepalive组件render的时候,先会拿到slot插槽元素,通过getFirstComponentChild函数获取到slot中的第一个元素(组件A)的vnode,获取到第一个元素的componentOptions中的tag,通过keepalive组件中的include和exclude条件判断如果不满足,render函数返回这个第一个子节点的vnode。
如果可以被缓存,下面会判断cache中是否已经缓存了这个元素,如果缓存过直接取出之前缓存的,如果没有缓存过就加入缓存。
最后给vnode.data.keepAlive赋值为true,返回vnode。
之后在_render函数中给vnode.parent赋值为原来的keepalive的vnode节点,然后返回:
之后进入patch函数,注意一下,keepalive组件由于在render阶段返回了它第一个子组件的vnode所以它是没有走patch的,而走了patch的组件A由于没有定义oldVnode,直接执行createElm,而不是像根vue实例一样走最下面的createElm:
进入createElm后,再进入createComponent函数:
同keepalive组件,组件A也会进入初始化逻辑:
但是与keepalive组件不同的是,组件A没有自带的render函数,所以在mount时,会通过compileToFunctions自己通过template模板生成render函数:
接下来还是会通过自己的渲染Watcher调用updateComponent来通过render函数生成vnode和渲染到真实DOM上,可以看到patch的时候,vnode根节点的tag已经是div了,即组件A内部的元素节点,而当前所在组件的_uid为2(右下角水印盖住了):
在patch中生成了组件A内容的真实DOM后执行insert钩子,然后通过vnode.elm返回真实DOM:
在从__patch__中返回的真实DOM被赋值给当前实例(组件A)的$el属性之后,从_update中返回:
返回到进入对组件A调用createComponent函数后,又执行了initComponent把新生成的真实DOM挂载到vnode.elm中,并且push到了insertedVnodeQueue数组中:
之后退出createElm函数:
返回到patch函数中调用了insert钩子,然后返回构建好的组件A的真正DOM:
在接着返回到的_update中,组件A的真实DOM被赋值给了组件_uid为1的组件,即为keepalive组件的$el属性,然后返回:
然后返回对keepalive组件调用createComponent方法,组件A的真实DOM会被挂载到keepalive组件的父节点中然后返回:
接着返回到根组件对三个子组件进行迭代调用createElm的地方:
返回到对根节点调用createElm后,通过insert函数,把#app节点的真实DOM插入到页面中的body元素中:
退出到patch函数中,通过removeVnodes函数移除了原来的子节点,下图右侧方框中为还没有解析的button节点和keep-alive节点: