《Vue.js 設計與實現》讀書筆記 - 第14章、內建組件和模塊

第14章、內建組件和模塊

14.1 KeepAlive 組件的實現原理

KeepAlive 一詞借鑑了 HTTP 協議。

KeepAlive 組件可以避免組件被頻繁的銷燬/重建。本質是緩存管理,再加上特殊的掛載卸載邏輯。

卸載時將組件放入另一個容器中,再次掛載時再拿出來。對應生命週期爲 activateddeactivated

我們創建內置組件 KeepAlive 後,需要在卸載和掛載時對它進行特殊處理。

const KeepAlive = {
  __isKeepAlive: true,
  setup(props, { slots }) {
    // 用於緩存組件實例
    const cache = new Map()
    // currentInstance 前幾章爲了生命週期存的全局變量
    // 記錄組件的實例
    const instance = currentInstance
    // 從實例的屬性中獲取一些渲染器的內部方法
    const { move, createElement } = instance.keepAliveCtx
    // 創建隱藏容器
    const storageContainer = createElement('div')
    instance._deActivate = (vnode) => {
      move(vnode, storageContainer)
    }
    instance._activate = (vnode) => {
      move(vnode, container, anchor)
    }

    return () => {
      // 默認插槽
      let rawVNode = slots.default()
      // 不是組件直接渲染 因爲非組件不能緩存
      if (typeof rawVNode.type !== 'object') {
        return rawVNode
      }

      const cachedVNode = cache.get(rawVNode.type)
      if (cachedVNode) {
        rawVNode.component = cachedVNode.component
        // 如果是緩存組件 後面就不需要再掛載初始化了
        rawVNode.keptAlive = true
      } else {
        cache.set(rawVNode.type, rawVNode)
      }

      rawVNode.shouldKeepAlive = true
      rawVNode.keepAliveInstance = instance

      return rawVNode
    }
  },
}

patch 的組件掛載邏輯部分新加判斷:

if (!n1) {
  if (n2.keptAlive) {
    n2.keepAliveInstance._activate(n2, container, anchor)
  }
  // 掛載
  mountComponent(n2, container, anchor)
}

卸載也一樣

function unmount(vnode) {
  if (vnode.type === Fragment) {
    // ...
  } else if (typeof vnode.type === 'object') {
    if (vnode.shouldKeepAlive) {
      // 如果是keepalive組件 卸載時調用組件的deactive方法
      vnode.keepAliveInstance._deActivate(vnode)
    } else {
      unmount(vnode.component.subTree)
    }
    return
  }
  // ...
}

同時在組件實例新增 keepAliveCtx,傳入 movecreateElement 讓組件在 activateddeactivated 時使用。

const instance = {
  state,
  props: shallowReactive(props),
  isMounted: false,
  subTree: null,
  slots,
  // 在組件實例中添加 mounted 數組,用來存儲通過 onMounted 函數註冊的生命週期函數
  mounted: [],
  // keepAlive組件實例下保存
  keepAliveCtx: null,
}
const isKeepAlive = vnode.type.__isKeepAlive
if (isKeepAlive) {
  instance.keepAliveCtx = {
    move(vnode, container, anchor) {
      insert(vnode.component, subTree.el, container, anchor)
    },
    createElement,
  }
}

KeepAlive 組件還支持 includeexclude 功能,實現原理就是在判斷是否緩存時加一個正則匹配。

緩存管理,需要限定緩存數量,在 Vue 中採取最近一次訪問優先策略。

以及考慮如何在 KeepAlive 中支持自定義策略。

14.2 Teleport 組件的實現原理

通常情況下,組件渲染成真實 DOM 的層級結構與虛擬 DOM 相同,但是如果我們想要渲染到其他位置就無法實現。比如我們想要渲染一個蒙層,需要掛載到 body 下面。

實現方式,給組件增加標識,patch 掛載時掛載到指定的位置即可。爲了方便 TreeShaking 把渲染邏輯封裝到組件內部,這樣如果用戶沒有使用到 Teleport 組件就不會加載相關代碼。

14.3 Transition 組件的實現原理

原理:

  • 組件被掛載時,將動效附加到該 DOM 上
  • 組件被卸載時,不要立即卸載 DOM 元素,等到 DOM 上動效執行完再卸載

在掛載時我們有 enter-fromenter-to 兩種,我們需要在開始時添加 enter-from 類名,然後在下一幀再添加 enter-to 觸發動畫。由於瀏覽器限制,我們要通過嵌套的 requestAnimationFrame 執行。

等到過渡完成,通過 transitionend 事件判斷,並刪除類。卸載同理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章