新聞中心
1.寫在前面
2.KeepAlive組件
KeepAlive字面意思理解就是保持鮮活,就是建立持久連接的意思,可以避免組件或連接頻繁地創(chuàng)建和銷毀。

創(chuàng)新互聯(lián)專注于察哈爾右翼中旗企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站開發(fā),成都商城網(wǎng)站開發(fā)。察哈爾右翼中旗網(wǎng)站建設(shè)公司,為察哈爾右翼中旗等地區(qū)提供建站服務(wù)。全流程按需定制開發(fā),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
在上面代碼中,會(huì)根據(jù)currentTab變量的值頻繁切換Tab組件,會(huì)導(dǎo)致不停地卸載和重建對應(yīng)的Tab組件,為了避免因此產(chǎn)生的性能開銷,可以使用KeepAlive組件保持組件的鮮活。那么KeepAlive組件是如何保持組件的鮮活的,其實(shí)就會(huì)對組件進(jìn)行緩存管理,避免組件頻繁的卸載和重建。
其實(shí),就是通過一個(gè)隱藏的組件緩存容器,將組件需要的時(shí)候?qū)⑵浞诺饺萜骼?,在需要重建使用的時(shí)候?qū)⑵淙〕?,這樣對于用戶感知是進(jìn)行了“卸載”和“重建”組件。在組件搬運(yùn)到緩存容器和搬出,就是對應(yīng)組件的生命周期activated和deactivated。
3.組件的失活和激活
那么,應(yīng)該如何實(shí)現(xiàn)組件的緩存管理呢?
const KeepAlive = {
// keepAlive組件的標(biāo)識符
_isKeepAlive:true,
setup(props,{slots}){
//緩存容器
const cache = new Map();
const instance = currentInstance;
const { move, createElement } = instance.keepAliveCtx;
//隱藏容器
const storageContainer = createElement("div");
instance._deActivate = (vnode)=>{
move(vnode, storageContainer)
};
instance._activate = (vnode, container, anchor)=>{
move(vnode, container, anchor)
};
return ()=>{
let rawNode = slots.default();
// 非組件的虛擬節(jié)點(diǎn)無法被keepAlive
if(typeof rawNode.type !== "object"){
return rawNode;
}
//在掛在時(shí)先獲取緩存的組件vnode
const cacheVNode = cache.get(rawNode.type);
if(cacheVNode){
rawVNode.component = cacheVNode.component;
rawVNode.keptAlive = true;
}else{
cache.set(rawVNode.type, rawVNode);
}
rawVNode.shouldKeepAlive = true;
rawVNode.keepAliveInstance = instance;
// 渲染組件vnode
return rawVNode
}
}
}在上面代碼中,KeepAlive組件本身不會(huì)渲染額外的內(nèi)容,渲染函數(shù)只返回被KeepAlive的組件,被稱為“內(nèi)部組件”,KeepAlive會(huì)在“內(nèi)部組件”的Vnode對象上添加標(biāo)記屬性,便于渲染器執(zhí)行特定邏輯。
- shouldKeepAlive屬性會(huì)被添加到“內(nèi)部組件”的vnode對象上,當(dāng)渲染器卸載“內(nèi)部組件”時(shí),可以通過檢查屬性得知“內(nèi)部組件”是否需要被KeepAlive。
- keepAliveInstance:內(nèi)部組件的vnode對象會(huì)持有keepAlive組件實(shí)例,在unmount函數(shù)中通過keepAliveInstance訪問_deactivate函數(shù)。
- keptAlive:內(nèi)部組件已被緩存則添加keptAlive標(biāo)記,判斷內(nèi)部組件重新渲染時(shí)是否需要重新掛載還是激活。
function unmount(vnode){
if(vnode.type === Fragment){
vnode.children.forEach(comp=>unmount(comp));
return;
}else if(typeof vnode.type === "object"){
if(vnode.shouldKeepAlive){
vnode.keepAliveInstance._deactivate(vnode);
}else{
unmount(vnode.component.subTree);
}
return
}
const parent = vnode.el.parentVNode;
if(parent){
parent.removeChild(vnode.el);
}
}組件失活的本質(zhì)是將組件所渲染的內(nèi)容移動(dòng)到隱藏容器中,激活的本質(zhì)是將組件所要渲染的內(nèi)容從隱藏容器中搬運(yùn)回原來的容器。
const { move, createElement } = instance.keepAliveCtx;
instance._deActivate = (vnode)=>{
move(vnode, storageContainer);
}
instance._activate = (vnode, container, anchor)=>{
move(vnode, container, anchor);
}
4.include和exclude
我們看到上面的代碼會(huì)對組件所有的"內(nèi)部組件"進(jìn)行緩存,但是使用者又想自定義緩存規(guī)則,只對特定組件進(jìn)行緩存,對此KeepAlive組件需要支持兩個(gè)props:include和exclude。
- include:用于顯式配置應(yīng)被緩存的組件
- exclude:用于顯式配置不應(yīng)該被緩存的組件
const cache = new Map();
const keepAlive = {
__isKeepAlive: true,
props:{
include: RegExp,
exclude: RegExp
},
setup(props, {slots}){
//...
return ()=>{
let rawVNode = slots.default();
if(typeof rawVNode.type !== "object"){
return rawVNode;
}
const name = rawVNode.type.name;
if(name && (
(props.include && !props.include.test(name)) ||
(props.exclude && props.include.test(name))
)){
//直接渲染內(nèi)部組件,不對其進(jìn)行緩存操作
return rawVNode
}
}
}
}
上面代碼中,為了簡便闡述問題進(jìn)行設(shè)置正則類型的值,在KeepAlive組件被掛載時(shí),會(huì)根據(jù)"內(nèi)部組件"的名稱進(jìn)行匹配,根據(jù)匹配結(jié)果判斷是否要對組件進(jìn)行緩存。
5.緩存管理
在前面小節(jié)中使用Map對象實(shí)現(xiàn)對組件的緩存,Map的鍵值對分別對應(yīng)的是組件vnode.type屬性值和描述該組件的vnode對象。因?yàn)橛糜诿枋鼋M件的vnode對象存在對組件實(shí)例的引用,對此緩存用于描述組件的vnode對象,等價(jià)于緩存了組件實(shí)例。
前面介紹的keepAlive組件實(shí)現(xiàn)緩存的處理邏輯是:
- 緩存存在時(shí)繼承組件實(shí)例,將描述組件的vnode對象標(biāo)記為keptAlive,渲染器不會(huì)重新創(chuàng)建新的組件實(shí)例
- 緩存不存在時(shí),則設(shè)置緩存
但是,如果緩存不存在時(shí),那么總是會(huì)設(shè)置新的緩存,這樣導(dǎo)致緩存不斷增加,會(huì)占用大量內(nèi)存。對此,我們需要設(shè)置個(gè)內(nèi)存閾值,在緩存數(shù)量超過指定閾值時(shí)需要對緩存進(jìn)行修剪,在Vue.js中使用的是"最新一次訪問"策略。
"最新一次訪問"策略本質(zhì)上就是通過設(shè)置當(dāng)前訪問或渲染的組件作為最新一次渲染的組件,并且該組件在修剪過程中始終是安全的,即不會(huì)被修剪。
緩存實(shí)例中需要滿足固定的格式:
const _cache = new Map();
const cache: KeepAliveCache = {
get(key){
_cache.get(key);
},
set(key, value){
_cache.set(key, value);
},
delete(key){
_cache.delete(key);
},
forEach(fn){
_cache.forEach(fn);
}
}
6.寫在最后
本文簡單介紹了Vue.js中KeepAlive組件的設(shè)計(jì)與實(shí)現(xiàn)原理,可以實(shí)現(xiàn)對組件的緩存,避免組件實(shí)例不斷地銷毀和重建。KeepAlive組件卸載時(shí)渲染器并不會(huì)真實(shí)地把它進(jìn)行卸載,而是將該組件搬運(yùn)到另外一個(gè)隱藏容器里,從而使得組件能夠維持當(dāng)前狀態(tài)。在KeepAlive組件掛載時(shí),渲染器將其從隱藏容器中搬運(yùn)到原容器中。此外,我們還討論了KeepAlive組件的include和exclude自定義緩存,以及緩存管理。
當(dāng)前標(biāo)題:Vue.js設(shè)計(jì)與實(shí)現(xiàn)18-KeepAlive的原理與實(shí)現(xiàn)
URL地址:http://fisionsoft.com.cn/article/cdcdhhi.html


咨詢
建站咨詢
