這是我參與更文挑戰的第4天,活動詳情查看: 更文挑戰vue
注:如下是我的理解、若有不對還望指正!node
包裹動態組件時,會緩存不活動的組件實例,而不是銷燬它們 是一個抽象組件:它自身不會渲染一個 DOM 元素,也不會出如今組件的父組件鏈中當組件在 內被切換,組件在活躍和不活躍觸發activated 和 deactivated 這兩個生命週期鉤子函數緩存
<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.js
app
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)
}
}
}
}
複製代碼
created () {
this.cache = Object.create(null)
this.keys = []
}
複製代碼
mounted () {
//監聽配置、重置緩存信息
this.$watch('include', val => {
pruneCache(this, name => matches(val, name))
})
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
})
}
複製代碼
//執行頁面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的位置在操做的) 、不存在的話往緩存對象添加記錄函數