vue中keep-alive的原理

1 、keep-alive是什么

keep-alive是一個抽象組件:它自身不會渲染一個DOM元素,也不會出現(xiàn)在父組件鏈中;使用keep-alive包裹動態(tài)組件時,會緩存不活動的組件實例,而不是銷毀它們。

二、源碼分析

// src/core/components/keep-alive.js
export default {
  name: 'keep-alive',
  abstract: true, // 判斷當前組件虛擬dom是否渲染成真實dom的關(guān)鍵
  props: {
      include: patternTypes, // 緩存白名單
      exclude: patternTypes, // 緩存黑名單
      max: [String, Number] // 緩存的組件
  },
  created() {
     this.cache = Object.create(null) // 緩存虛擬dom
     this.keys = [] // 緩存的虛擬dom的鍵集合
  },
  destroyed() {
    for (const key in this.cache) {
       // 刪除所有的緩存
       pruneCacheEntry(this.cache, key, this.keys)
    }
  },
 mounted() {
   // 實時監(jiān)聽黑白名單的變動
   this.$watch('include', val => {
       pruneCache(this, name => matched(val, name))
   })
   this.$watch('exclude', val => {
       pruneCache(this, name => !matches(val, name))
   })
 },

 render() {
    // 先省略...
 }
}

keep-alive在它生命周期內(nèi)定義了三個鉤子函數(shù):

created

初始化兩個對象分別緩存VNode(虛擬DOM)和VNode對應(yīng)的鍵集合

destroyed

刪除this.cache中緩存的VNode實例。我們留意到,這不是簡單地將this.cache置為null,而是遍歷調(diào)用pruneCacheEntry函數(shù)刪除。
刪除緩存的VNode還要對應(yīng)組件實例的destory鉤子函數(shù)

// 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.$destroyed() // 執(zhí)行組件的destroy鉤子函數(shù)
 }
 cache[key] = null
 remove(keys, key)
}

mounted

在mounted這個鉤子中對include和exclude參數(shù)進行監(jiān)聽,然后實時地更新(刪除)this.cache對象數(shù)據(jù)。pruneCache函數(shù)的核心也是去調(diào)用pruneCacheEntry

render階段
  • 獲取keep-alive包裹著的第一個子組件對象及其組件名;

  • 根據(jù)設(shè)定的黑白名單(如果有)進行條件匹配,決定是否緩存。不匹配,直接返回組件實例(VNode),否則執(zhí)行第三步;

  • 根據(jù)組件ID和tag生成緩存Key,并在緩存對象中查找是否已緩存過該組件實例。如果存在,直接取出緩存值并更新該key在this.keys中的位置(更新key的位置是實現(xiàn)LRU置換策略的關(guān)鍵),否則執(zhí)行第四步;

  • 在this.cache對象中存儲該組件實例并保存key值,之后檢查緩存的實例數(shù)量是否超過max設(shè)置值,超過則根據(jù)LRU置換策略刪除最近最久未使用的實例(即是下標為0的那個key);

  • 最后并且很重要,將該組件實例的keepAlive屬性值設(shè)置為true。

render () {
  const slot = this.$slots.defalut
  const vnode: VNode = getFirstComponentChild(slot) // 找到第一個子組件對象
  const componentOptions : ?VNodeComponentOptions = vnode && vnode.componentOptions
  if (componentOptions) { // 存在組件參數(shù)
    // check pattern
    const name: ?string = getComponentName(componentOptions) // 組件名
    const { include, exclude } = this
    if (// 條件匹配
      // not included
      (include && (!name || !matches(include, name)))||
      // excluded
        (exclude && name && matches(exclude, name))
    ) {
        return vnode
    }
    
    const { cache, keys } = this
    // 定義組件的緩存key
    const key: ?string = vnode.key === null ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '') : vnode.key
     if (cache[key]) { // 已經(jīng)緩存過該組件
        vnode.componentInstance = cache[key].componentInstance
        remove(keys, key)
        keys.push(key) // 調(diào)整key排序
     } else {
        cache[key] = vnode //緩存組件對象
        keys.push(key)
        if (this.max && keys.length > parseInt(this.max)) {
          //超過緩存數(shù)限制,將第一個刪除
          pruneCacheEntry(cahce, keys[0], keys, this._vnode)
        }
     }
     
      vnode.data.keepAlive = true //渲染和執(zhí)行被包裹組件的鉤子函數(shù)需要用到
 
 }
 return vnode || (slot && slot[0])
}

鏈接:http://www.itdecent.cn/p/9523bb439950

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容