vue2.x中keep-alive源碼解析(實例代碼)
一、前世塵緣
vue中內(nèi)置組件keep-alive的設(shè)計思想源于HTTP中的Keep-Alive模式,Keep-Alive模式避免頻繁創(chuàng)建、銷毀鏈接,允許多個請求和響應(yīng)使用同一個HTTP鏈接。
HTTP 1.0 中keep-alive默認是關(guān)閉的,需要在HTTP頭加入"Connection: Keep-Alive",才能啟用Keep-Alive;HTTP 1.1中默認啟用Keep-Alive,如果加入"Connection: close ",才關(guān)閉。目前大部分瀏覽器都是用HTTP 1.1協(xié)議。
二、keep-alive內(nèi)置組件
作用:動態(tài)切換組件時緩存組件實例,避免dom重新渲染。
1.緩存動態(tài)組件
當組件為componentOne
時緩存該組件實例
<keep-alive :include="componentOne`" :exclude="componentTwo" :max="num"> <component :is="currentComponent"></component> </keep-alive>
2.緩存路由組件
注意緩存路由組件vue2.x與vue3.x有區(qū)別,vue2.x用法如下:
<keep-alive :include="componentOne`" :exclude="componentTwo" :max="num"> <router-view :is="currentComponent"></router-view> </keep-alive>
vue3.x用法如下:
<router-view v-slot="{ Component }"> <keep-alive :include="includeList"> <component :is="Component"/> </keep-alive> </router-view>
3.原理解析
緩存的組件以 [key,vnode] 的形式記錄,keys記錄緩存的組件key,依據(jù)inclued、exclude的值,并且當超過設(shè)置的max根據(jù)LUR算法進行清除。vue2.x和vue3.x相差不大。
(1)keep-alive 在生命周期中做了什么?
- created:初始化catch,keys。catch是一個緩存組件虛擬dom的數(shù)組,其中數(shù)組中對象的key是組件的key,value是組件的虛擬dom;keys是一個用來緩存組件的key的數(shù)組。
- mounted:實時監(jiān)聽include、exclude屬性的變化,并執(zhí)行相應(yīng)操作。
- destroyed:刪除掉所有緩存相關(guān)的數(shù)據(jù)。
(2)源碼
地址:源碼地址
// 源碼位置:src/core/components/keep-alive.js export default { name: 'keep-alive', abstract: true, props: { include: patternTypes, exclude: patternTypes, max: [String, Number] }, created () { this.cache = Object.create(null) this.keys = [] }, destroyed () { for (const key in this.cache) { pruneCacheEntry(this.cache, key, this.keys) } }, mounted () { //查看是否有緩存沒有緩存的話直接走緩存 this.cacheVNode() // 這里借助 watch 監(jiān)控 include 和 exclude // 如果有變化的話,則按照最新的 include 和 exclude 更新 this.cache // 將不滿足 include、exclude 限制的 緩存vnode 從 this.cache 中移除 this.$watch('include', val => { pruneCache(this, name => matches(val, name)) }) this.$watch('exclude', val => { pruneCache(this, name => !matches(val, name)) }) }, updated() { this.cacheVNode() }, methods:{ cacheVNode() { const { cache, keys, vnodeToCache, keyToCache } = this if (vnodeToCache) { const { tag, componentInstance, componentOptions } = vnodeToCache cache[keyToCache] = { name: _getComponentName(componentOptions), tag, componentInstance } keys.push(keyToCache) // prune oldest entry if (this.max && keys.length > parseInt(this.max)) { pruneCacheEntry(cache, keys[0], keys, this._vnode) } this.vnodeToCache = null } } }, render(){ //下面詳細介紹 } }
(3)abstract:true
設(shè)置為true時,表面該組件為抽象組件,抽象組件不會和子組件建立父子關(guān)系,組件實例會根據(jù)這個屬性決定是否忽略該組件,所以并不會有節(jié)點渲染在頁面中。
(4)pruneCacheEntry函數(shù)
destoryed周期中循環(huán)了所有緩存的組件,并用 pruneCacheEntry
進行處理,pruneCacheEntry做了什么事?
// src/core/components/keep-alive.js function pruneCacheEntry ( cache: VNodeCache, key: string, keys: Array<string>, current?: VNode ) { const cached = cache[key] if (cached && (!current || cached.tag !== current.tag)) { cached.componentInstance.$destroy() // 執(zhí)行組件的destory鉤子函數(shù) } cache[key] = null // cache中對象的key設(shè)為null remove(keys, key) // 刪除keys對應(yīng)的元素 }
destoryed周期中,刪除緩存組件的所有數(shù)組,pruneCacheEntry
主要做了這幾件事:
- 遍歷緩存組件集合(cach),對所有緩存的組件執(zhí)行$destroy方法
- 清除cache中key的值
- 清除keys中的key
(5)render
render中主要做了什么?
- 獲取keep-alive組件子節(jié)點中第一個組件的vnode、componentOptions、name
- 如果name存在且不在include中或者存在在exclude中,則返回虛擬dom。此時該組件并沒有使用緩存。
- 接下來就是上面的else情況:使用keep-alive進行組件緩存,根據(jù)組件id,tag生成組件的key,如果cache集合中存在以key為屬性名的vdom,,說明組件已經(jīng)緩存過,則將緩存的 Vue 實例賦值給 vnode.componentInstance,從keys中刪除key,再把key push導(dǎo)keys中,保證當前key在keys的最后面(這是LRU算法的關(guān)鍵)。如果不存在則繼續(xù)走下面
- 如果cach[key]不存在則為第一次加載組件,則把vdom賦值給cach[key],key push到key
- 如果keys的長度大于max,則進行組件緩存清理,則把不經(jīng)常使用的被緩存下來的在keys中排第一位的組件清除掉,清除也是調(diào)用的pruneCacheEntry方法
render () { // 獲取 keep-alive 組件子節(jié)點中的第一個組件 vnode const slot = this.$slots.default const vnode = getFirstComponentChild(slot) // 獲取組件的配置選項對象 const componentOptions = vnode && vnode.componentOptions if (componentOptions) { // 獲取組件的名稱 const name = _getComponentName(componentOptions) const { include, exclude } = this // 如果當前的組件 name 不在 include 中或者組件的 name 在 exclude 中 // 說明當前的組件是不被 keep-alive 所緩存的,此時直接 return vnode 即可 if ( // not included (include && (!name || !matches(include, name))) || // excluded (exclude && name && matches(exclude, name)) ) { return vnode } // 代碼執(zhí)行到這里,說明當前的組件受 keep-alive 組件的緩存 const { cache, keys } = this // 定義 vnode 緩存用的 key const key = vnode.key == null ? // same constructor may get registered as different local components // so cid alone is not enough (#3269) componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '') : vnode.key // 如果 cache[key] 已經(jīng)存在的話,則說明當前的組件 vnode 已經(jīng)被緩存過了,此時需要將其恢復(fù)還原出來 if (cache[key]) { // 將緩存的 Vue 實例賦值給 vnode.componentInstance vnode.componentInstance = cache[key].componentInstance // make current key freshest // 先從 keys 中移除 key,然后再 push key,這可以保證當前的 key 在 keys 數(shù)組中的最后面 remove(keys, key) keys.push(key) } else { // delay setting the cache until update // 如果 cache[key] 不存在的話,說明當前的子組件是第一次出現(xiàn),此時需要將 vnode 緩存到 cache 中,將 key 存儲到 keys 字符串數(shù)組中。這里是用一個中間變量接收,當數(shù)據(jù)變化時觸發(fā)updated去調(diào)用cacheVNode方法。 this.vnodeToCache = vnode this.keyToCache = key } // @ts-expect-error can vnode.data can be undefined // 將 vnode.data.keepAlive 屬性設(shè)置為 true,這對 vnode 有一個標識的作用,標識這個 // vnode 是 keep-alive 組件的 render 函數(shù) return 出去的,這個標識在下面的運行代碼中有用 vnode.data.keepAlive = true } return vnode || (slot && slot[0]) }
三、LRU算法
緩存的組件在進行清除的時候使用了LRU算法,具體是什么策略呢?當數(shù)據(jù)超過了限定空間的時候?qū)?shù)據(jù)清理,清理的原則是對很久沒有使用到過的數(shù)據(jù)進行清除。
到此這篇關(guān)于vue2.x中keep-alive源碼解析的文章就介紹到這了,更多相關(guān)vue2.x keep-alive源碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
elementPlus中的Autocomplete彈出層錯位問題解決分析
這篇文章主要介紹了elementPlus中的Autocomplete彈出層錯位問題解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07