使用
<!-- 基本 -->
<keep-alive>
<component :is="view"></component>
</keep-alive>
<keep-alive>
包裹動態組件時,會緩存不活動的組件實例,而不是銷毀它們。和 <transition>
相似,<keep-alive>
是一個抽象組件:它自身不會渲染一個 DOM 元素,也不會出現在父組件鏈中。
當組件在 <keep-alive>
內被切換,它的 activated
和 deactivated
這兩個生命周期鈎子函數將會被對應執行。
原理
/* keep-alive組件 */
export default {
name: 'keep-alive',
/* 抽象組件 */
abstract: true,
props: {
include: patternTypes,
exclude: patternTypes
},
created () {
/* 緩存對象 */
this.cache = Object.create(null)
},
/* destroyed鈎子中銷毀所有cache中的組件實例 */
destroyed () {
for (const key in this.cache) {
pruneCacheEntry(this.cache[key])
}
},
watch: {
/* 監視include以及exclude,在被修改的時候對cache進行修正 */
include (val: string | RegExp) {
pruneCache(this.cache, this._vnode, name => matches(val, name))
},
exclude (val: string | RegExp) {
pruneCache(this.cache, this._vnode, name => !matches(val, name))
}
},
render () {
/* 得到slot插槽中的第一個組件 */
const vnode: VNode = getFirstComponentChild(this.$slots.default)
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// check pattern
/* 獲取組件名稱,優先獲取組件的name字段,否則是組件的tag */
const name: ?string = getComponentName(componentOptions)
/* name不在inlcude中或者在exlude中則直接返回vnode(沒有取緩存) */
if (name && (
(this.include && !matches(this.include, name)) ||
(this.exclude && matches(this.exclude, name))
)) {
return vnode
}
const key: ?string = vnode.key == null
// same constructor may get registered as different local components
// so cid alone is not enough (#3269)
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key
/* 如果已經做過緩存了則直接從緩存中獲取組件實例給vnode,還未緩存過則進行緩存 */
if (this.cache[key]) {
vnode.componentInstance = this.cache[key].componentInstance
// 2.5.0 新增這段邏輯,使用 LRU 策略 make current key freshest
remove(keys, key);
keys.push(key);
}
// 不命中緩存,把 vnode 設置進緩存
else {
this.cache[key] = vnode;
// 2.5.0 新增這段邏輯,LRU 策略的移除。
keys.push(key);
// 如果配置了 max 並且緩存的長度超過了 this.max,還要從緩存中刪除第一個
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode);
}
}
/* keepAlive標記位 */
vnode.data.keepAlive = true
}
return vnode
}
}
- 獲取 keep-alive 包裹着的第一個子組件對象及其組件名
根據設定的 include/exclude(如果有)進行條件匹配,決定是否緩存。不匹配,直接返回組件實例 - 根據組件 ID 和 tag 生成緩存 Key,並在緩存對象中查找是否已緩存過該組件實例。如果存在,直接取出緩存值並更新該 key 在 this.keys 中的位置(更新 key 的位置是實現 LRU 置換策略的關鍵)
- 在 this.cache 對象中存儲該組件實例並保存 key 值,之后檢查緩存的實例數量是否超過 max 的設置值,超過則根據 LRU 置換策略刪除最近最久未使用的實例(即是下標為 0 的那個 key)
- 最后組件實例的 keepAlive 屬性設置為 true,這個在渲染和執行被包裹組件的鈎子函數會用到,
LRU 策略
最近最久未使用算法。
每次從內存中找出最久的未使用數據用於置換新的數據。