当前位置: 首页 > Web前端 > vue.js

「Vue源码学习」简单讲一讲keep-alive的原理吧

时间:2023-04-01 01:11:23 vue.js

简单说一下《Vue源码学习》中keep-alive的原理是我的初衷。回想起来,刚开始写的时候写的是Vue源码系列,全部收录在我的掘金专栏Vue源码解析:《Vue源码学习(一)》你所不知道的——Vue数据响应原理源码学习(二)《你所不知道的——模板编译原理》Vue源码学习(三)》你所不知道的——第一种渲染原理《Vue源码学习(四)》立志写一个computed大家都能看懂的文章,看原理《Vue源码学习(五)》面试官喜欢问-Vue常用方法源码分析Vue源码学习》想了解Vuex的实现原理吗?《Vue源码学习》你真的知道槽是怎么“插”的吗?15张图,20分钟看懂Diff算法核心原理,我说!!!林三心画了8张图,最通俗易懂的Vue3响应式核心原理解析7张图,从头实现了一个简单版的Vue-Router,太通俗易懂了!今天就给大家说说Vue中常用组件keep-alive的基本原理吧!场景你在平时的开发中可能经常会遇到这样的场景:有一个列表页面List.vue可以进行筛选,点击某一项会进入对应的详情页面。从详情页返回List.vue时,发现列表页刷新了!刚才的过滤条件没有了!!!什么是保活?keep-alive是一个Vue全局组件。keep-alive本身不会被渲染,也不会出现在父组件链中。keep-alive包裹动态组件时,会缓存不活跃的组件,而不是销毁它们。如何使用它?keep-alive接受三个参数:包括:可以传递字符串、正则表达式和数组。名称匹配成功的组件将被缓存。exclude:可以传递字符串、正则表达式和数组。名称匹配成功的组件将不会被缓存。Cachemax:可以通过数字来限制缓存组件include和exclude的最大数量,大部分是动态组件路由组件源码组件基础前面提到keep-alive是一个Vue全局组件,接收三个参数:include:Strings,exclude:可以传递字符串、正则表达式、数组,不缓存名称匹配成功的组件,最大个数,如果超过max,会按照LRU算法进行替换,顺便说一下keep-alive在每个生命周期中是干什么的:created:初始化一个缓存和keys,前者用来存放缓存组件的虚拟dom集合,后者用于存储挂载的缓存组件的key集合:实时监控include和exclude的变化,并执行相应的操作destroyed:删除所有缓存相关的东西,之前说过keep-alive不会渲染到页面,soabstract这个属性很重要!//src/core/components/keep-alive.jsexportdefault{name:'keep-alive',abstract:true,//判断这个组件是否需要渲染成真实的DOMprops:{include:patternTypes,exclude:patternTypes,max:[String,Number]},created(){this.cache=Object.create(null)//创建一个对象来存储缓存virtualdomthis.keys=[]//创建一个数组来存储缓存key},mounted(){//实时监控包含和排除变化this.$watch('include',val=>{pruneCache(this,name=>matches(val,name))})this.$watch('排除',val=>{pruneCache(this,name=>!matches(val,name))})},destroyed(){for(constkeyinthis.cache){//删除所有缓存pruneCacheEntry(this.cache,key,this.keys)}},render(){//下面说说吧}}在我们上面实现的pruneCacheEntry函数销毁的生命周期中,进行了删除所有缓存的操作,而这个操作是通过调用pruneCacheEntry来实现的,下面说说关于pruneCacheEntry//src/core/components/keep-alive.jsfunctionpruneCacheEntry(cache:VNodeCache,key:string,keys:Array,current?:VNode){constcached=cache[key]if(cached&&(!current||cached.tag!==current.tag)){cached.componentInstance.$destroy()//执行组件的destroy钩子函数}cache[key]=null//设置为nullremove(keys,key)//删除对应的元素}总结起来就是做了三件事:1.遍历集合,执行所有缓存组件的$destroy方法2.设置key的内容correspondingtothecachetonull3.删除keys中对应的keyelementrenderfunction以下称include为白名单,exclude为黑名单。render函数主要做了这些事情:Step1:获取keep-alive包的第一个组件及其组件名称Step2:判断这个组件名称是否可以被白名单或黑名单匹配,如果不能被白名单||能被黑名单匹配到,就直接返回给VNode,不再执行。如果不匹配,进行第三步。根据组件ID,tag生成一个缓存key,在缓存集合中查找这个组件是否缓存过如果已经缓存了,直接取出缓存组件,更新缓存key在keys中的位置(这是LRU算法的key),如果还没有缓存,继续第4步。第4步:保存thisincacheandkeys分别是组件及其缓存key,并检查数量是否超过max,如果超过则根据LRU算法删除第五步:将这个组件实例的keepAlive属性设置为true,这个很重要,并且会在下面提到!//src/core/components/keep-alive.jsrender(){constslot=this.$slots.defaultconstvnode:VNode=getFirstComponentChild(slot)//找到第一个子组件对象constcomponentOptions:?VNodeComponentOptions=vnode&&vnode.componentOptionsif(componentOptions){//有组件参数//检查模式constname:?string=getComponentName(componentOptions)//组件名称const{include,exclude}=thisif(//条件匹配//不包含(include&&(!name||!matches(include,name)))||//excluded(exclude&&name&&matches(exclude,name))){returnvnode}const{cache,keys}=这个常量键:?string=vnode.key==null//定义组件的缓存键//相同的构造函数可能会注册为不同的本地组件//所以只有cid是不够的(#3269)?componentOptions.Ctor.cid+(componentOptions.tag?`::${componentOptions.tag}`:''):vnode.keyif(cache[key]){//组件已经被缓存vnode.componentInstance=cache[key].componentInstance//使当前key最新鲜remove(keys,key)keys.push(key)//调整key排序}else{cache[key]=vnode//缓存Component对象keys.push(key)//剪枝oldestentryif(this.max&&keys.length>parseInt(this.max)){//超过缓存限制,删除第一个pruneCacheEntry(cache,keys[0],keys,this._vnode)}}vnode.data.keepAlive=true//渲染执行被包裹组件的钩子函数需要用到}returnvnode||(slot&&slot[0])}rendering我们先来看一个Vue组件是如何渲染的,我们先从render开始:根据第一次渲染时得到的VNode,第二次渲染开始时,会将该VNode与旧的VNode进行比较,打补丁(diff算法比较发生在这个阶段),然后渲染成真实的DOMkeep-alive本身渲染刚才说了,keep-alive本身的组件不会在页面上渲染,那是怎么做到的呢?其实通过判断组件实例上abstract的属性值,如果为true,则跳过该实例,该实例不会出现在父链中On//src/core/instance/lifecycle.jsexportfunctioninitLifecycle(vm:Component){constoptions=vm.$options//找到第一个非抽象父组件实例letparent=options.parentif(parent&&!options.abstract){while(parent.$options.abstract&&parent.$parent){parent=parent.$parent}parent.$children.push(vm)}vm.$parent=parent//...}packageComponentrendering下面说说keep-alive包裹的组件如何使用缓存刚才说了VNode->realDOM发生在patch阶段,其实这也细分为:VNode->instantiation->_update->realDOM,组件使用缓存的判断发生在实例化阶段,而createComponent函数在这个阶段被调用,所以让我们谈谈这个函数://src/core/vdom/patch.jsfunctioncreateComponent(vnode,insertedVnodeQueue,parentElm,refElm){leti=vnode.dataif(isDef(i)){constisReactivated=isDef(vnode.componentInstance)&&i.keepAliveif(isDef(i=i.hook)&&isDef(i=i.init)){i(vnode,false/*hydrating*/)}if(isDef(vnode.componentInstance)){initComponent(vnode,insertedVnodeQueue)insert(parentElm,vnode.elm,refElm)//将缓存的DOM(vnode.elm)插入到父元素中if(isTrue(isReactivated)){reactivateComponent(vnode,insertedVnodeQueue,parentElm,refElm)}returntrue}}}第一次加载被包裹的组件时,因为之前执行了keep-alive的renderwrappedcomponent加载完毕,此时vnode.componentInstance的值为undefined,keepAlive为true,然后代码转到i(vnode,false/*hydrating*/),停止往下走。再次访问包组件时,vnode.componentInstance该值为已经缓存的组件实例,然后会执行insert(parentElm,vnode.elm,refElm)的逻辑,从而将最后一个DOM直接插入到父元素中。结语我是林三鑫,一个狂热的前端菜鸟程序员。如果你有上进心,喜欢前端,想学前端,那我们可以交个朋友一起钓鱼哈哈,鱼群参考透彻揭秘keep-alive原理vue技术揭秘|keep-aliveVue源码代码

猜你喜欢