vue-keep-alive源碼分析

這是我參與更文挑戰的第4天,活動詳情查看: 更文挑戰vue

注:如下是我的理解、若有不對還望指正!node

包裹動態組件時,會緩存不活動的組件實例,而不是銷燬它們 是一個抽象組件:它自身不會渲染一個 DOM 元素,也不會出如今組件的父組件鏈中當組件在 內被切換,組件在活躍和不活躍觸發activated 和 deactivated 這兩個生命週期鉤子函數緩存

keep-alive使用

  • 需求 完成對一個組件進行緩存配置
<div id="app">
    <keep-alive include="test"> <test></test> <demo></demo> </keep-alive>
</div>

const test = {
    name:'test',
    template:"<div> {{ componentsName }} </div>",
    data(){
        return {
            componentName:'test測試組件'
        }
    },
    activated(){
        console.log('test-activated')
    }
}

const demo = {
    name:'demo',
    template:"<div> {{ name }} </div>",
    data(){
        return {
            componentName:'demo測試組件'
        }
    },
    activated(){
        console.log('demo_keep-alive')
    }
}

const vm = new Vue({
    components:{
        test,
        demo
    },
    el:'#app'
})
複製代碼

咱們發現頁面只會對第一個test組件進行展現、奇怪了爲何我只有test呢、那demo組件去哪裏了呢?帶着這些疑問咱們打開vue的源碼看下知其因此然....markdown

源碼分析

我會把主要源碼貼入進來、包括代碼的註釋、組件文件地址:/src/core/components/keep-alive.jsapp

  • 判斷配置和當前組件名稱是否匹配
function matches (pattern: string | RegExp | Array<string>, name: string): boolean {
  if (Array.isArray(pattern)) {
    return pattern.indexOf(name) > -1
  } else if (typeof pattern === 'string') {
    return pattern.split(',').indexOf(name) > -1
  } else if (isRegExp(pattern)) {
    return pattern.test(name)
  }
  /* istanbul ignore next */
  return false
}
複製代碼
  • 匹配條件、清除緩存
function pruneCache (keepAliveInstance: any, filter: Function) {
  const { cache, keys, _vnode } = keepAliveInstance
  for (const key in cache) {
    const cachedNode: ?VNode = cache[key]
    if (cachedNode) {
      const name: ?string = getComponentName(cachedNode.componentOptions)
      if (name && !filter(name)) {
        pruneCacheEntry(cache, key, keys, _vnode)
      }
    }
  }
}
複製代碼
  • 建立緩存對象和保存組件key
created () {
    this.cache = Object.create(null)
    this.keys = []
 }
複製代碼
  • watch監聽配置變化
mounted () {
    //監聽配置、重置緩存信息
    this.$watch('include', val => {
      pruneCache(this, name => matches(val, name))
    })
    this.$watch('exclude', val => {
      pruneCache(this, name => !matches(val, name))
    })
  }
複製代碼
  • 執行render方法
//執行頁面render
  render () {
    const slot = this.$slots.default
    //獲取kepp-alive組件下的第一個子組件vndoe
    const vnode: VNode = getFirstComponentChild(slot)
    const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
    if (componentOptions) {
      // 獲取組件名稱
      const name: ?string = getComponentName(componentOptions)
      const { include, exclude } = this;
      //判斷是不是須要緩存、不須要直接走這if
      if (
        // 有include和沒有獲取到name值 或者 include是否包含name值
        (include && (!name || !matches(include, name))) ||
        // 是不是白名單、直接過濾
        (exclude && name && matches(exclude, name))
      ) {
        return vnode
      }
      //須要緩存邏輯
      const { cache, keys } = this
      //判斷是否有key、若是沒有vue會自動給他加上key
      const key: ?string = vnode.key == null
        ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
        : vnode.key
      //當前是否已經有緩存下來的組件數據、有直接取緩存的
      if (cache[key]) {
          //賦值緩存的vnode
        vnode.componentInstance = cache[key].componentInstance
        // 從新調整組件key的位置、目的是爲了若是數據最近被訪問過,那麼未來被訪問的概率也更高
        remove(keys, key)
        keys.push(key)
      } else {
        //保存緩存vnode數據
        cache[key] = vnode
        //添加key
        keys.push(key)
        // 判斷是否超過最大緩存值
        if (this.max && keys.length > parseInt(this.max)) {
          //超過就刪除第一個保存的vnode
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
      }
      //添加keepAlive = true標記
      vnode.data.keepAlive = true
    }
    return vnode || (slot && slot[0])
  }
複製代碼

總結

大體的緩存流程,vue有個內置組件、這個組件維護着緩存對象和被緩存組件的key名稱、提升你傳入的include, exclude判斷是否須要緩存當前組件、並且咱們在render函數發現組件永遠都只會取第一個子組件內容、而咱們案例上的demo組件永遠沒有機會顯示出來、其實也有辦法那就是給他們包裹vue提供的另一個內置組件component、判斷顯示那個組件、而後keep-alive會提升當前組件是否設置了白名單或者不是include配置項組件那就直接return vnode,遇到緩存的vnode、先判斷緩存對象是否已經存若是存在直接取緩存vnode (有個小細節、從新調整組件key的位置、目的是爲了若是數據最近被訪問過,那麼未來被訪問的概率也更高、由於可能緩存到必定max數量、會遇到刪除棧的vnode、這個時候是根據key的位置在操做的) 、不存在的話往緩存對象添加記錄函數

相關文章
相關標籤/搜索