vue源码阅读—13—扩展之keeplive
一、keep-live内置组件在什么时候注册的?
在加载Vue类的时候,会引入src/core/index.js文件,然后这里会引调用nitGlobalAPI方法;
二、keep-live的首次挂载阶段
keep-llive自身redner函数的实现
keep-live是如何渲染到dom的?
keep-live是一个抽象组件,它的abstract属性为true,
所以它本身是没有模板要渲染的,它的render函数返回的vnode是keep-live组件第一个子组件的vnode;
然后给第一个子组件vnode.data.keeplive = true;
所以可以理解为keeplive有组件实例,但它的vnode是第一个子组件vnode;
2.1首次patch
父组件div在patch时会先让子组件patch,所以执行createChildren;
然后对第一个子组件keeplive执行creaElm;
因为keeplive是组件所以执行这个;
然后执行这个,去实例化孙子组件component,所以孙子组件就被patch真实dom了。
也就是执行child = vnode.componentInstance = createComponentInstanceForVnode,为什么不执行第一个if,因为这个时候vnode.componentInstacne是undefined;
只有执行了creatComponentInstaceForVnode,vnode.componentInstacne才会有值;
你看名字,createComponentInstanceForVnode也可以理解,见名知意,为vnode创建一个组件实例;
子组件实例创建好后,去调用child.$mount;
这个时候又会执行keeplive组件的render函数了;
主要是通过插槽获取第一个子组件,这里也就是子组件A;
在ptach阶段,因为keeplive的vnode是A组件的,所以执行的是a组件的patch过程,
然后a组件就被正常的创建出来了。
并不是!由于我的源码版本和老师的vue源码版本不一样,所以至于a组件怎么创建出来的并不清楚,自己大dubugger研究吧。
老师的代码是调用reactiveComponent的insert代码把元素插入进去,因为我的vue版本和老师的不一样的,所以我的keeplive子组件的插入是按照普通流程来的
三、keeplive组件的重新渲染
3.1首次重新渲染,从a到b
我们点击按钮,触发click事件,改变currentComp变量的数据为b,然后派发更新,导致渲染watcher重新渲染;
父组件组件会重新patch过程
然后会调用这一步,因为他们的子组件都是keeplive都是一样的;
所以继续子组件即keeplive的patchVnode过程;
然后 先执行keeplive的prepatch方法;
然后执行updateChi的Component方法;
updateChildComponent方法里,keeplive组件的hasChildren为true;
第一步,更新keeplive的插槽,也就是第一个子组件,去重新获取它的第一个子组件;
第二步,让keeplive的实例vm重新渲染;
重新渲染,则调用vm._update(vm._render()); 然后会重新调用keeplive组件的render函数;
然后执行keeplive组件的patch阶段;
注意这个keeplive组件的vnode是调用render函数返回的vnode,和keeplive组件占位符vnode不一样;
这个时候oldVnode就是"vue-component-1-A",vnode就是vue-component-3-B"
然后执行这一步,也就是把b组件节点重新创建出来,由于b组件是首次创建,所以和上面a组件重新创建时是一样的流程,就不赘述了。
我们接下来看看b组件节点创建好之后的事;
然后把a组件节点删除掉;
然后开始执行b组件的钩子函数;
先执行callhook(mounted)
接下来会执行queueActivatedComponent方法,这个方法会执行callhook('activated'),具体见上文,已经说过了不再赘述;
3.1再次"重新渲染",从b又回到a
我们再点击click把数据改回来又为a组件了,
没啥不同,就是在keeplive的渲染阶段,即执行render函数时,
通过缓存把vm实例取到赋值给vnode.componentInstance;
四、keeplive的生命周期函数
4.1actived钩子函数
在组件patch结束后,会执行invokeInsertHooks,
它会调用inset这个钩子函数;
(一)如果子组件还没有挂载,所以我们执行activeChildCompnent方法,
这个方法主要就是调用callhook(active)这个钩子;(它也会递归遍历它的children,然后执行同样执行activeChildComonent;也就是同样children也调用actived这个钩子函数)
(二)如果子组件已经挂载,走 queueActiveComponent方法;
把实例._inactive置为false;
然后把vm添加到activetedChildren中;
那么这个activedChildren什么时候会执行?
先通过slice方法获取一个activeChildren的副本;
然后callActivatedHooks(activatedQueue)
callActivedHooks就是遍历queue,然后再次执行activateChildComponent;
然后和第一个一样,主要就是callHook activated这个钩子;
以上就是patch阶段actived钩子函数的执行;
所以keeplive下的a组件的钩子执行过程是:
beforeCreated、create、beforeMounted、 mounted、acitvated、deactivated;
4.2destory钩子函数
那如果keeplive组件要销毁,destory钩子函数什么时候执行?
调用componentVNodeHooks.destory方法;
对普通组件会执行componentInstance.$destroy;
对keeplive组件会执行deactiveChildComponent;
调用callHook(deactived);
五、总结