一文詳解Vue中keep-alive的實(shí)現(xiàn)原理
引言
在 Vue.js 開發(fā)單頁(yè)面應(yīng)用(SPA)時(shí),組件的頻繁創(chuàng)建和銷毀會(huì)帶來(lái)一定的性能開銷。為了優(yōu)化這一問(wèn)題,Vue 提供了 keep-alive 組件。keep-alive 可以將包裹在其中的組件實(shí)例進(jìn)行緩存,避免重復(fù)創(chuàng)建和銷毀,從而提升應(yīng)用的性能和用戶體驗(yàn)。本文將深入剖析 keep-alive 的實(shí)現(xiàn)原理。
keep-alive 的基本使用
keep-alive 是一個(gè)內(nèi)置組件,使用時(shí)只需將需要緩存的組件包裹在 <keep-alive> 標(biāo)簽內(nèi)。示例如下:
<template>
<div>
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
<button @click="toggleComponent">切換組件</button>
</div>
</template>
<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';
export default {
data() {
return {
currentComponent: 'ComponentA'
};
},
components: {
ComponentA,
ComponentB
},
methods: {
toggleComponent() {
this.currentComponent = this.currentComponent === 'ComponentA' ? 'ComponentB' : 'ComponentA';
}
}
};
</script>
在上述代碼中,ComponentA 和 ComponentB 會(huì)被 keep-alive 緩存,切換時(shí)不會(huì)重新創(chuàng)建。
keep-alive 的實(shí)現(xiàn)原理
1. 緩存機(jī)制
keep-alive 內(nèi)部使用一個(gè)對(duì)象 cache 來(lái)存儲(chǔ)緩存的組件實(shí)例,鍵為組件的唯一標(biāo)識(shí),值為組件實(shí)例。同時(shí),使用一個(gè)數(shù)組 keys 來(lái)存儲(chǔ)這些組件的鍵,用于管理緩存的順序。
2. 組件渲染過(guò)程
當(dāng) keep-alive 包裹的組件首次渲染時(shí),keep-alive 會(huì)正常創(chuàng)建組件實(shí)例,并將其緩存到 cache 對(duì)象中,同時(shí)將對(duì)應(yīng)的鍵添加到 keys 數(shù)組。當(dāng)再次渲染該組件時(shí),keep-alive 會(huì)從 cache 中取出緩存的組件實(shí)例進(jìn)行渲染,而不是重新創(chuàng)建。
3. 生命周期鉤子
keep-alive 會(huì)影響組件的生命周期鉤子。被緩存的組件在首次進(jìn)入時(shí)會(huì)觸發(fā) activated 鉤子,在離開時(shí)會(huì)觸發(fā) deactivated 鉤子,而不是 mounted 和 destroyed。這是因?yàn)榻M件實(shí)例并沒(méi)有被真正銷毀,只是被隱藏了起來(lái)。
4. 源碼分析
以下是簡(jiǎn)化后的 keep-alive 源碼分析:
export default {
name: 'keep-alive',
abstract: true, // 抽象組件,不會(huì)渲染到 DOM 中
props: {
include: [String, RegExp, Array], // 包含的組件名稱
exclude: [String, RegExp, Array], // 排除的組件名稱
max: [String, Number] // 最大緩存數(shù)量
},
created() {
this.cache = Object.create(null); // 初始化緩存對(duì)象
this.keys = []; // 初始化鍵數(shù)組
},
destroyed() {
for (const key in this.cache) {
// 銷毀緩存的組件實(shí)例
this.pruneCacheEntry(this.cache[key]);
}
},
mounted() {
// 監(jiān)聽 include 和 exclude 的變化
this.$watch('include', val => {
this.pruneCache(name => matches(val, name));
});
this.$watch('exclude', val => {
this.pruneCache(name =>!matches(val, name));
});
},
render() {
const vnode = getFirstComponentChild(this.$slots.default); // 獲取第一個(gè)子組件的虛擬節(jié)點(diǎn)
const componentOptions = vnode && vnode.componentOptions;
if (componentOptions) {
const name = getComponentName(componentOptions); // 獲取組件名稱
const { include, exclude } = this;
if (
// 判斷是否需要緩存
(include && (!name ||!matches(include, name))) ||
(exclude && name && matches(exclude, name))
) {
return vnode;
}
const { cache, keys } = this;
const key = vnode.key == null
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key;
if (cache[key]) {
// 從緩存中獲取組件實(shí)例
vnode.componentInstance = cache[key].componentInstance;
// 調(diào)整緩存順序
remove(keys, key);
keys.push(key);
} else {
// 緩存新的組件實(shí)例
cache[key] = vnode;
keys.push(key);
// 超過(guò)最大緩存數(shù)量時(shí),移除最早的緩存
if (this.max && keys.length > parseInt(this.max)) {
this.pruneCacheEntry(keys[0]);
}
}
vnode.data.keepAlive = true; // 標(biāo)記組件被緩存
}
return vnode;
},
methods: {
pruneCache(filter) {
for (const key in this.cache) {
const cachedNode = this.cache[key];
if (cachedNode) {
const name = getComponentName(cachedNode.componentOptions);
if (name &&!filter(name)) {
this.pruneCacheEntry(cachedNode);
}
}
}
},
pruneCacheEntry(vnode) {
if (vnode) {
// 銷毀組件實(shí)例
vnode.componentInstance.$destroy();
this.cache[vnode.key] = null;
remove(this.keys, vnode.key);
}
}
}
};
代碼解釋
abstract: true:表明keep-alive是一個(gè)抽象組件,不會(huì)渲染到 DOM 中。created鉤子:初始化cache對(duì)象和keys數(shù)組。destroyed鉤子:銷毀所有緩存的組件實(shí)例。mounted鉤子:監(jiān)聽include和exclude的變化,根據(jù)條件清理緩存。render方法:- 獲取第一個(gè)子組件的虛擬節(jié)點(diǎn)。
- 判斷是否需要緩存該組件。
- 如果組件已緩存,從緩存中獲取實(shí)例并調(diào)整緩存順序。
- 如果組件未緩存,將其添加到緩存中,并根據(jù)
max屬性判斷是否需要移除最早的緩存。 - 標(biāo)記組件被緩存。
pruneCache方法:根據(jù)過(guò)濾條件清理緩存。pruneCacheEntry方法:銷毀指定的組件實(shí)例并從緩存中移除。
總結(jié)
keep-alive 通過(guò)內(nèi)部的緩存機(jī)制,避免了組件的重復(fù)創(chuàng)建和銷毀,提升了應(yīng)用的性能。它通過(guò) cache 對(duì)象和 keys 數(shù)組來(lái)管理緩存,同時(shí)影響組件的生命周期鉤子。理解 keep-alive 的實(shí)現(xiàn)原理,有助于我們?cè)陂_發(fā)中更好地使用它,優(yōu)化應(yīng)用的性能和用戶體驗(yàn)。
以上就是一文詳解Vue中keep-alive的實(shí)現(xiàn)原理的詳細(xì)內(nèi)容,更多關(guān)于Vue keep-alive實(shí)現(xiàn)原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue改變數(shù)據(jù)后數(shù)據(jù)變化頁(yè)面不刷新的解決方法
這篇文章主要給大家介紹了關(guān)于vue改變數(shù)據(jù)后數(shù)據(jù)變化頁(yè)面不刷新的解決方法,vue比較常見的坑就是數(shù)據(jù)(后臺(tái)返回)更新了,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07
Vue中使用ECharts與v-if的問(wèn)題和解決方案
在Vue項(xiàng)目中使用v-if指令控制ECharts圖表顯示時(shí),可能會(huì)遇到圖表無(wú)法正常渲染或顯示錯(cuò)誤的問(wèn)題,下面這篇文章主要介紹了Vue中使用ECharts與v-if的問(wèn)題和解決方案,需要的朋友可以參考下2024-10-10
Vue3+NodeJS+Soket.io實(shí)現(xiàn)實(shí)時(shí)聊天的示例代碼
本文主要介紹了Vue3+NodeJS+Soket.io實(shí)現(xiàn)實(shí)時(shí)聊天的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
vue.js實(shí)例對(duì)象+組件樹的詳細(xì)介紹
這篇文章主要介紹了vue.js實(shí)例對(duì)象+組件樹的相關(guān)資料,需要的朋友可以參考下2017-10-10
Vue.config.js配置報(bào)錯(cuò)ValidationError:?Invalid?options?object解
這篇文章主要給大家介紹了關(guān)于Vue.config.js配置報(bào)錯(cuò)ValidationError:?Invalid?options?object的解決辦法,主要由于vue.config.js配置文件錯(cuò)誤導(dǎo)致的,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-02-02

