第14章、內建組件和模塊
14.1 KeepAlive 組件的實現原理
KeepAlive 一詞借鑑了 HTTP 協議。
KeepAlive 組件可以避免組件被頻繁的銷燬/重建。本質是緩存管理,再加上特殊的掛載卸載邏輯。
卸載時將組件放入另一個容器中,再次掛載時再拿出來。對應生命週期爲 activated
和 deactivated
。
我們創建內置組件 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
,傳入 move
和 createElement
讓組件在 activated
和 deactivated
時使用。
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 組件還支持 include
和 exclude
功能,實現原理就是在判斷是否緩存時加一個正則匹配。
緩存管理,需要限定緩存數量,在 Vue 中採取最近一次訪問優先策略。
以及考慮如何在 KeepAlive 中支持自定義策略。
14.2 Teleport 組件的實現原理
通常情況下,組件渲染成真實 DOM 的層級結構與虛擬 DOM 相同,但是如果我們想要渲染到其他位置就無法實現。比如我們想要渲染一個蒙層,需要掛載到 body 下面。
實現方式,給組件增加標識,patch
掛載時掛載到指定的位置即可。爲了方便 TreeShaking 把渲染邏輯封裝到組件內部,這樣如果用戶沒有使用到 Teleport 組件就不會加載相關代碼。
14.3 Transition 組件的實現原理
原理:
- 組件被掛載時,將動效附加到該 DOM 上
- 組件被卸載時,不要立即卸載 DOM 元素,等到 DOM 上動效執行完再卸載
在掛載時我們有 enter-from
和 enter-to
兩種,我們需要在開始時添加 enter-from
類名,然後在下一幀再添加 enter-to
觸發動畫。由於瀏覽器限制,我們要通過嵌套的 requestAnimationFrame
執行。
等到過渡完成,通過 transitionend
事件判斷,並刪除類。卸載同理。