Vue keep-alive的实现原理是什么
Vue keep-alive的实现原理是什么
这篇“Vuekeep-alive的实现原理是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Vuekeep-alive的实现原理是什么”文章吧。
keep-alive的实现原理
使用vue的时候,想必大家都是用过keep-alive,其作用就是缓存页面以及其状态。使用了这么久vue只知道如何使用但不明白其中原理,昨天翻看实现代码,这里做个笔记。
这里以vue3为例
整个组件的源码为:
constKeepAliveImpl={name:`KeepAlive`,//Markerforspecialhandlinginsidetherenderer.Wearenotusinga===//checkdirectlyonKeepAliveintherenderer,becauseimportingitdirectly//wouldpreventitfrombeingtree-shaken.__isKeepAlive:true,props:{include:[String,RegExp,Array],exclude:[String,RegExp,Array],max:[String,Number]},setup(props:KeepAliveProps,{slots}:SetupContext){constcache:Cache=newMap()constkeys:Keys=newSet()letcurrent:VNode|null=nullconstinstance=getCurrentInstance()!//console.log('instance',instance)//KeepAlivecommunicateswiththeinstantiatedrendererviathe"sink"//wheretherendererpassesinplatform-specificfunctions,andthe//KeepAliveinstanceexposesactivate/deactivateimplementations.//ThewholepointofthisistoavoidimportingKeepAlivedirectlyinthe//renderertofacilitatetree-shaking.constsink=instance.sinkasKeepAliveSinkconst{renderer:{move,unmount:_unmount,options:{createElement}},parentSuspense}=sinkconststorageContainer=createElement('div')//console.log('sink',sink)sink.activate=(vnode,container,anchor)=>{move(vnode,container,anchor,MoveType.ENTER,parentSuspense)queuePostRenderEffect(()=>{constcomponent=vnode.component!component.isDeactivated=falseif(component.a!==null){invokeHooks(component.a)}},parentSuspense)}sink.deactivate=(vnode:VNode)=>{move(vnode,storageContainer,null,MoveType.LEAVE,parentSuspense)queuePostRenderEffect(()=>{constcomponent=vnode.component!if(component.da!==null){invokeHooks(component.da)}component.isDeactivated=true},parentSuspense)}functionunmount(vnode:VNode){//resettheshapeFlagsoitcanbeproperlyunmountedvnode.shapeFlag=ShapeFlags.STATEFUL_COMPONENT_unmount(vnode,instance,parentSuspense)}functionpruneCache(filter?:(name:string)=>boolean){cache.forEach((vnode,key)=>{constname=getName(vnode.typeasComponent)if(name&&(!filter||!filter(name))){pruneCacheEntry(key)}})}functionpruneCacheEntry(key:CacheKey){constcached=cache.get(key)asVNodeif(!current||cached.type!==current.type){unmount(cached)}elseif(current){//currentactiveinstanceshouldnolongerbekept-alive.//wecan'tunmountitnowbutitmightbelater,soresetitsflagnow.current.shapeFlag=ShapeFlags.STATEFUL_COMPONENT}cache.delete(key)keys.delete(key)}watch(()=>[props.include,props.exclude],([include,exclude])=>{include&&pruneCache(name=>matches(include,name))exclude&&pruneCache(name=>matches(exclude,name))},{lazy:true})onBeforeUnmount(()=>{cache.forEach(unmount)})return()=>{if(!slots.default){returnnull}constchildren=slots.default()letvnode=children[0]if(children.length>1){if(__DEV__){warn(`KeepAliveshouldcontainexactlyonecomponentchild.`)}current=nullreturnchildren}elseif(!isVNode(vnode)||!(vnode.shapeFlag&ShapeFlags.STATEFUL_COMPONENT)){current=nullreturnvnode}constcomp=vnode.typeasComponentconstname=getName(comp)const{include,exclude,max}=propsif((include&&(!name||!matches(include,name)))||(exclude&&name&&matches(exclude,name))){returnvnode}constkey=vnode.key==null?comp:vnode.keyconstcached=cache.get(key)//clonevnodeifit'sreusedbecausewearegoingtomutateitif(vnode.el){vnode=cloneVNode(vnode)}cache.set(key,vnode)if(cached){//copyovermountedstatevnode.el=cached.elvnode.anchor=cached.anchorvnode.component=cached.componentif(vnode.transition){//recursivelyupdatetransitionhooksonsubTreesetTransitionHooks(vnode,vnode.transition!)}//avoidvnodebeingmountedasfreshvnode.shapeFlag|=ShapeFlags.COMPONENT_KEPT_ALIVE//makethiskeythefreshestkeys.delete(key)keys.add(key)}else{keys.add(key)//pruneoldestentryif(max&&keys.size>parseInt(maxasstring,10)){pruneCacheEntry(Array.from(keys)[0])}}//avoidvnodebeingunmountedvnode.shapeFlag|=ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVEcurrent=vnodereturnvnode}}}
很容易看出keep-alive其实就是vue自己封装的一个组件,和普通组件一样。
再讲keep-alive组件前先了解下vue组件的整个渲染
大致流程如下
keep-alive生命周期
组件挂载:
调用setupStatefulComponent函数触发组件setup方法,其中组件的setup方法核心代码其实就几行:
return()=>{constchildren=slots.default()letvnode=children[0]cache.set(key,vnode)if(cached){vnode.el=cached.elvnode.anchor=cached.anchorvnode.component=cached.componentvnode.shapeFlag|=ShapeFlags.COMPONENT_KEPT_ALIVEkeys.delete(key)keys.add(key)}else{keys.add(key)}returnvnode}
主要逻辑为三:
1.确认需要渲染的slot、
2.将其状态置入缓存或读取已存在的缓存、
3.返回slot对应的vnode,紧接着调用setupRenderEffect,渲染出dom。
组件更新(slot变化):
当slot变化后,首先会调用keep-alive组件的render即setup的返回函数,逻辑见上面setup方法。紧接着当某个slot卸载时,会调用deactivate函数,当某个slot重新挂载时,则会调用activate函数,核心代码如下:
conststorageContainer=createElement('div')sink.activate=(vnode,container,anchor)=>{move(vnode,container,anchor,MoveType.ENTER,parentSuspense)queuePostRenderEffect(()=>{constcomponent=vnode.component!component.isDeactivated=falseif(component.a!==null){invokeHooks(component.a)}},parentSuspense)}sink.deactivate=(vnode:VNode)=>{move(vnode,storageContainer,null,MoveType.LEAVE,parentSuspense)queuePostRenderEffect(()=>{constcomponent=vnode.component!if(component.da!==null){invokeHooks(component.da)}component.isDeactivated=true},parentSuspense)}
逻辑也很简单,当组件卸载时,将其移入缓存的dom节点中,调用slot的deactivate生命周期,当组件重新挂载时候,将其移入至挂载的dom节点中。
总结来说,keep-alive实现原理就是将对应的状态放入一个cache对象中,对应的dom节点放入缓存dom中,当下次再次需要渲染时,从对象中获取状态,从缓存dom中移出至挂载dom节点中。
keep-alive的使用总结
在平常开发中,有些组件只需要加载一次,后面的数据将不存在变化,亦或者是组件需要缓存状态,滚动条位置等,这个时候,keep-alive的用处就立刻凸显出来了。
1.App.vue中使用keep-alive
include表示需要缓存的页面,exclude表示不需要缓存的页面,你可以只设置其中一个即可,但两个同时设置的时候,切记exclude优先级高于include,例如a组件在exclude中和include中都存在,那么,a组件是不会被缓存的
<template><divid="app"><keep-alive:include="whiteList":exclude="blackList"><router-viewv-if="isRouterAlive"></router-view></keep-alive></div></template>
<script>exportdefault{name:'App',data(){return{isRouterAlive:true,whiteList:['styleLibrary','OrderList','SalesData'],blackList:['Footer'],personShow:false,}},}</script>
2.App.vue中配合router进行使用
<template><divid="app"><keep-alive><router-viewv-if="$route.meta.keepAlive"></router-view><!--缓存组件--></keep-alive><router-viewv-if="!$route.meta.keepAlive"></router-view><!--非缓存组件--></div></template>
将需要缓存的组件的$route.meta中的keepAlive设置为true,反之为false
{path:'/login',name:'login',component:resolve=>require(['@/pages/login'],resolve),meta:{keepAlive:true,title:'登录',savedPosition:true,}},
以上就是关于“Vuekeep-alive的实现原理是什么”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注恰卡编程网行业资讯频道。
推荐阅读
-
vue表格组件教程学习(vue proxytable只能在开发环境跨域吗)
vueproxytable只能在开发环境跨域吗?跨域问题来源于JavaScript的同源策略,即只有协议主机名端口号(如...
-
Vue组件的自定义事件和全局事件总线怎么使用
-
vue中消息订阅与发布如何使用
vue中消息订阅与发布如何使用这篇文章主要介绍“vue中消息订阅与...
-
Vue显示图片的方式有哪些
-
vue引入静态jquery报错如何解决
vue引入静态jquery报错如何解决这篇文章主要介绍“vue引入...
-
vue-cropper怎么实现裁剪图片
-
怎么用Vue+NodeJS实现大文件上传
-
Vue如何实现简易跑马灯效果
-
Vue怎么指定不编译的文件夹和favicon.ico
Vue怎么指定不编译的文件夹和favicon.ico这篇文章主要介...
-
Vue中的插槽怎么使用