切換語言為:簡體

Vue 的 keep-alive 底層原理解析

  • 爱糖宝
  • 2024-08-15
  • 2057
  • 0
  • 0

什麼是 keep-alive

keep-alive 是 Vue 的一個內建抽象元件,通常用於快取動態元件路由元件。被 keep-alive 包裹的元件在切換時不會被銷燬,而是會被快取下來,下一次切換回這個元件時,會直接複用之前的例項,保持其狀態。

<template>
  <keep-alive>
    <component :is="view"></component>
  </keep-alive>
</template>

<script>
export default {
  data() {
    return {
      view: 'MyComponent'
    };
  }
};
</script>

在這個例子中,MyComponent 在被切換時不會被銷燬,而是會被快取,當再次展示時,狀態和資料都保持不變。

重要的 keep-alive 配置選項 🚀🚀

keep-alive 提供了幾個有用的屬性和鉤子:

  1. includeexclude: 用於控制哪些元件需要快取,支援字串、正規表示式或陣列。

    <keep-alive include="ComponentA, ComponentB" exclude="ComponentC">
      <router-view></router-view>
    </keep-alive>

  2. max: 用於指定快取的元件數量,當超出這個數量時,最久未使用的元件例項將被銷燬。

    <keep-alive :max="10">
      <router-view></router-view>
    </keep-alive>

生命週期鉤子 🚀🚀

keep-alive 還引入了兩個新的元件生命週期鉤子,用於處理快取元件:

  • activated:當元件被啟用時觸發(即從快取中恢復時)。

  • deactivated:當元件被停用時觸發(即被快取時)。

export default {
  activated() {
    console.log('元件被啟用了');
  },
  deactivated() {
    console.log('元件被快取了');
  }
};

適用場景

  1. 多頁籤(Tab)切換:在複雜的表單或多步操作場景中,使用者可能會頻繁切換頁面。如果使用 keep-alive,切換回來的頁面能保留之前輸入的資料或操作的狀態。

  2. 路由快取:在 Vue 專案中,常常會在路由切換時希望保持元件狀態,如商品詳情頁、搜索結果頁等。

加分項:

  1. 我們需要適度使用,雖然 keep-alive 可以提升效能,但並不是所有元件都適合快取。如果元件需要頻繁更新資料或依賴實時性,快取反而會導致不必要的複雜性。

  2. 最好是配合路由使用,在 Vue Router 中,使用 keep-alive 可以避免在切換路由時重新載入元件資料,提升使用者體驗。

  3. 如果專案中有大量需要快取的頁面或元件,可以結合 maxexclude 來更好地管理快取。

keep-alive 的核心原理 🚀🚀

簡單來說,keep-alive 是透過快取元件例項來避免元件重複建立和銷燬,達到效能最佳化的目的。它透過將已快取的元件儲存在記憶體中,當元件被重新啟用時,直接複用之前快取的例項,而不是重新建立。

核心原理步驟:

  1. 快取例項:當元件被第一次載入時,keep-alive 會將元件的例項快取起來。

  2. 元件複用:當你切換到一個已經被快取的元件時,keep-alive 會從快取中提取該元件的例項,而不是重新建立。

  3. 生命週期管理:爲了處理元件的啟用和停用,keep-alive 引入了 activateddeactivated 鉤子,在元件進入或離開快取時觸發。

keep-alive 的原始碼分析

以下是我抽取了一些關鍵程式碼和原理解析:

export default {
  name: 'KeepAlive',
  abstract: true, // 這是一個抽象元件,表示它不會直接渲染到 DOM 上

  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);
    }
  },

  watch: {
    include (val) {
      pruneCache(this, name => matches(val, name));
    },
    exclude (val) {
      pruneCache(this, name => !matches(val, name));
    }
  },

  render () {
    const slot = this.$slots.default;
    const vnode = getFirstComponentChild(slot); // 獲取第一個子元件

    if (vnode) {
      const componentOptions = vnode.componentOptions;
      const name = getComponentName(componentOptions);
      
      if (name && (
        (this.include && !matches(this.include, name)) ||
        (this.exclude && matches(this.exclude, name))
      )) {
        return vnode; // 如果不匹配 include/exclude,直接返回,不快取
      }

      const key = vnode.key == null
        ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
        : vnode.key;

      if (this.cache[key]) {
        vnode.componentInstance = this.cache[key].componentInstance; // 從快取中取出例項
        remove(this.keys, key); // 移除舊的位置
        this.keys.push(key); // 重新放到最後,更新 LRU 位置
      } else {
        this.cache[key] = vnode; // 快取新例項
        this.keys.push(key);

        // 如果超過最大快取數,移除最早的例項
        if (this.max && this.keys.length > parseInt(this.max)) {
          pruneCacheEntry(this.cache, this.keys[0], this.keys, this._vnode);
        }
      }

      vnode.data.keepAlive = true; // 標記元件為 keep-alive
    }

    return vnode || (slot && slot[0]); // 返回 vnode
  }
};

1. 快取機制:

this.cache 是一個物件,用於儲存已經快取的元件例項。

this.keys 是一個數組,用來記錄快取元件的順序(實現 LRU 快取策略,具體見下方)。

2. 元件的快取和啟用:

render 函式中,Vue 判斷當前元件是否在 includeexclude 的範圍內。如果匹配不到,則不進行快取。

透過 key 標識元件,並將其與快取例項關聯。如果元件已經在快取中,直接取出快取的元件例項並複用。

3. LRU 快取策略:

LRU 快取策略維持了一個有序的數據結構,記錄了快取項的使用順序。當快取達到容量限制時,它會移除最近最少使用的項,以便為新的資料騰出空間。常見的實現方式包括使用雙向連結串列和雜湊表的組合,來保持快取項的順序和快速訪問。

this.keys 的長度超過 max 時,刪除最早的快取元件(即 this.keys[0])。

4. 生命週期的管理:

activateddeactivated 生命週期鉤子與 keep-alive 緊密相關,它們在元件被從快取中啟用和停用時觸發。

activateddeactivated 鉤子用於管理元件的啟用和停用。

上面這 4 方面是 keep-alive 的核心原理程式碼。

總結

希望透過這篇文章,你對 Vue 的 keep-alive 元件有了更深入的理解。

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.