vue源码阅读—13—扩展之keeplive


 

一、keep-live内置组件在什么时候注册的?

在加载Vue类的时候,会引入src/core/index.js文件,然后这里会引调用nitGlobalAPI方法;

builtInComponents变量就是KeepAlive内置组件;所以这个方法主要是把内置组件keep-live扩展到Vue.options.components上;
然后通过实例的创建初始化化阶段,会有一个Vue.options和自定义options的合并,然后都赋值给vm.$options,
所以每一个实例自身就有内置组价了。

 

 

 

 

 

 

 

 

 

二、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这个钩子函数;

因coponentInsatcne._isMounted是false,所以会先调用callHook(componentInsatnce,'mounted');即先执行mounted这个钩子函数;
因为keeplive子组件的vnode.data.keelive为true,所以命中这一块的逻辑;
如果子组件已经挂挂载,调用queueActivatedComponent

(一)如果子组件还没有挂载,所以我们执行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);

 

 

 

 

 

 

 

 

五、总结

 

posted @ 2022-08-13 17:34  Eric-Shen  阅读(668)  评论(0编辑  收藏  举报