這篇文章主要介紹“Vue中的KeepAlive組件怎么使用”,在日常操作中,相信很多人在Vue中的KeepAlive組件怎么使用問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Vue中的KeepAlive組件怎么使用”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
順平ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書(shū)未來(lái)市場(chǎng)廣闊!成為創(chuàng)新互聯(lián)建站的ssl證書(shū)銷售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書(shū)合作)期待與您的合作!
<KeepAlive>
是一個(gè)內(nèi)置組件,它的功能是在多個(gè)組件間動(dòng)態(tài)切換時(shí)緩存被移除的組件實(shí)例。
KeepAlive 一詞借鑒于 HTTP 協(xié)議,在 HTTP 協(xié)議里面 KeepAlive 又稱持久連接,作用是允許多個(gè)請(qǐng)求/響應(yīng)共用同一個(gè) HTTP 連接,解決了頻繁的銷毀和創(chuàng)建 HTTP 連接帶來(lái)的額外性能開(kāi)銷。而同理 Vue 里的 KeepAlive 組件也是為了避免一個(gè)組件被頻繁的銷毀/重建,避免了性能上的開(kāi)銷。
// App.vue
<Test :msg="curTab" v-if="curTab === 'Test'"></Test>
<HelloWorld :msg="curTab" v-if="curTab === 'HelloWorld'"></HelloWorld>
<div @click="toggle">toggle</div>
上述代碼可以看到,如果我們頻繁點(diǎn)擊 toggle 時(shí)會(huì)頻繁的渲染 Test/HelloWorld 組件,當(dāng)用戶頻繁的點(diǎn)擊時(shí) Test 組件需要頻繁的銷毀/渲染,這就造成很大的渲染性能損失。
所以為了解決這種性能開(kāi)銷,你需要知道是時(shí)候使用 KeepAlive 組件。
<KeepAlive>
<component :is="curTab === 'Test' ? Test : HelloWorld" :msg="curTab"></component>
</KeepAlive>
<div @click="toggle">toggle</div>
可以看這個(gè)錄屏,在首次加載后再次頻繁的切換并沒(méi)有重新銷毀與掛載,而僅僅是將組件進(jìn)行了失活(而不是銷毀),渲染時(shí)只需要重新激活就可以,而不需重新掛載,如果要渲染的組件很大,那就能有不錯(cuò)的性能優(yōu)化。
想要體驗(yàn)的話可以去看看這個(gè)例子?官方demo,其中數(shù)據(jù)會(huì)被緩存這個(gè)也需要在開(kāi)發(fā)使用中去注意到的
實(shí)現(xiàn)原理其實(shí)很簡(jiǎn)單,其實(shí)就是緩存管理和特定的銷毀和渲染邏輯,使得它不同于其他組件。
KeepAlive 組件在卸載組件時(shí)并不能真的將其卸載,而是將其放到一個(gè)隱藏的容器里面,當(dāng)被激活時(shí)再?gòu)碾[藏的容器中拿出來(lái)掛載到真正的 dom 上就行,這也就對(duì)應(yīng)了 KeepAlive 的兩個(gè)獨(dú)特的生命周期activated
和deactivated
。
所以在 KeepAlive 內(nèi)的子組件在 mount 和 unmount 的時(shí)候會(huì)執(zhí)行特定的渲染邏輯,從而不會(huì)去走掛載和銷毀邏輯
const KeepAliveImpl: ComponentOptions = {
name: "KeepAlive",
// 標(biāo)識(shí)這是一個(gè) KeepAlive 組件
__isKeepAlive: true,
// props
props: {
exclude: [String, Array, RegExp],
include: [String, Array, RegExp],
max: [String, Number]
}
}
// isKeepAlive
export const isKeepAlive = (vnode: VNode): boolean =>
(vnode.type as any).__isKeepAlive
// setup 接著上面的代碼
// 獲取到當(dāng)前 KeepAlive 組件實(shí)例
const instance = getCurrentInstance()! as any;
// 拿到 ctx
const sharedContext = instance.ctx as KeepAliveContext;
// cache 緩存
// key: vnode.key | vnode.type value: vnode
const cache: Cache = new Map()
// 需要拿到某些的 renderer 操作函數(shù),需要自己特定執(zhí)行渲染和卸載邏輯
const { renderer: { p: patch, m: move, um: _unmount, o: { createElement } } } = sharedContext
// 隱藏的容器,用來(lái)存儲(chǔ)需要隱藏的 dom
const storeageContainer = createElement('div')
// 存儲(chǔ)當(dāng)前的子組件的緩存 key
let pendingKey: CacheKey | null = null
sharedContext.activate = (vnode, container, anchor) => {
// KeepAlive 下組件激活時(shí)執(zhí)行的 move 邏輯
move(vnode, container, anchor, 0 /* ENTER */)
}
sharedContext.deactivate = (vnode) => {
// KeepAlive 下組件失活時(shí)執(zhí)行的 move 邏輯
move(vnode, storeageContainer, null, 1 /* LEAVE */)
}
return () => {
// 沒(méi)有子組件
if (!slots.default) {
return null;
}
const children = slots.default() as VNode[];
const rawNode = children[0];
let vnode = rawNode;
const comp = vnode.type as ConcreteComponent;
const name = comp.displayName || comp.name
const { include, exclude } = props;
// 沒(méi)有命中的情況
if (
(include && (!name || !matches(include, name))) ||
(exclude && name && matches(exclude, name))
) {
// 直接渲染子組件
return rawNode;
}
// 獲取子組件的 vnode key
const key = vnode.key == null ? comp : vnode.key;
// 獲取子組件緩存的 vnode
const cachedVNode = cache.get(key);
pendingKey = key;
// 命中緩存
if (cachedVNode) {
vnode.el = cachedVNode.el;
// 繼承組件實(shí)例
vnode.component = cachedVNode.component;
// 在 vnode 上更新 shapeFlag,標(biāo)記為 COMPONENT_KEPT_ALIVE 屬性,防止渲染器重新掛載
vnode.shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE
} else {
// 沒(méi)命中將其緩存
cache.set(pendingKey, vnode)
}
// 在 vnode 上更新 shapeFlag,標(biāo)記為 COMPONENT_SHOULD_KEEP_ALIVE 屬性,防止渲染器將組件卸載了
vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
// 渲染組件 vnode
return vnode;
}
在 KeepAlive 組件內(nèi)會(huì)從 sharedContext 上的 renderer 上拿到一些方法比如 move、createElement 等
function mountComponent() {
// ...
if (isKeepAlive(initialVNode)) {
;(instance.ctx as KeepAliveContext).renderer = internals
}
}
首先從上面可以看到,在渲染 KeepAlive 組件時(shí)會(huì)對(duì)其子組件的 vnode 上增加對(duì)應(yīng)的 shapeFlag 標(biāo)志
比如COMPONENT_KEPT_ALIVE
標(biāo)志,組件掛載的時(shí)候告訴渲染器這個(gè)不需要 mount 而需要特殊處理
const processComponent = (
n1: VNode | null,
n2: VNode,
container: RendererElement,
anchor: RendererNode | null,
) => {
if (n1 == null) {
// 在 KeepAlive 組件渲染時(shí)會(huì)對(duì)子組件增加 COMPONENT_KEPT_ALIVE 標(biāo)志
// 掛載子組件時(shí)會(huì)判斷是否 COMPONENT_KEPT_ALIVE ,如果是不會(huì)調(diào)用 mountComponent 而是直接執(zhí)行 activate 方法
if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
;(parentComponent!.ctx as KeepAliveContext).activate(
n2,
container,
anchor
)
}
// ...
}
}
同理COMPONENT_SHOULD_KEEP_ALIVE
標(biāo)志也是用來(lái)在組件卸載的時(shí)候告訴渲染器這個(gè)不需要 unmount 而需要特殊處理。
const unmount: UnmountFn = (vnode) => {
// ...
// 在 KeepAlive 組件渲染時(shí)會(huì)對(duì)子組件增加 COMPONENT_SHOULD_KEEP_ALIVE 標(biāo)志
// 然后在子組件卸載時(shí)并不會(huì)真實(shí)的卸載而是調(diào)用 KeepAlive 的 deactivate 方法
if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
;(parentComponent!.ctx as KeepAliveContext).deactivate(vnode)
return
}
}
activated
和deactivated
生命周期(生命周期相關(guān)可以不用重點(diǎn)看)首先這兩個(gè)生命周期是在 KeepAlive 組件內(nèi)獨(dú)特聲明的,是直接導(dǎo)出使用的。
export function onActivated(
hook: Function,
target?: ComponentInternalInstance | null
) {
// 注冊(cè) activated 的回調(diào)函數(shù)到當(dāng)前的 instance 的鉤子函數(shù)上
registerKeepAliveHook(hook, LifecycleHooks.ACTIVATED, target)
}
export function onDeactivated(
hook: Function,
target?: ComponentInternalInstance | null
) {
// 注冊(cè) deactivated 的回調(diào)函數(shù)到當(dāng)前的 instance 的鉤子函數(shù)上
registerKeepAliveHook(hook, LifecycleHooks.DEACTIVATED, target)
}
然后因?yàn)檫@兩個(gè)生命周期會(huì)注冊(cè)在 setup 里面,所以只要執(zhí)行 setup 就會(huì)將兩個(gè)生命周期的回調(diào)函數(shù)注冊(cè)到當(dāng)前的 instance 實(shí)例上
// renderer.ts
// mount 函數(shù)邏輯
const mountComponent = (initialVNode,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
) => {
// ...
const instance: ComponentInternalInstance =
compatMountInstance ||
(initialVNode.component = createComponentInstance(
initialVNode,
parentComponent,
parentSuspense
))
// 執(zhí)行 setup
setupComponent(instance)
}
// setupcomponent 處理 setup 函數(shù)值
export function setupComponent(
instance: ComponentInternalInstance,
isSSR = false
) {
// ...
const isStateful = isStatefulComponent(instance)
// ...
const setupResult = isStateful
// setupStatefulComponent 函數(shù)主要功能是設(shè)置當(dāng)前的 instance
? setupStatefulComponent(instance, isSSR)
: undefined
// ...
}
function setupStatefulComponent(
instance: ComponentInternalInstance
){
if (setup) {
//設(shè)置當(dāng)前實(shí)例
setCurrentInstance(instance)
// 執(zhí)行組件內(nèi) setup 函數(shù),執(zhí)行 onActivated 鉤子函數(shù)進(jìn)行回調(diào)函數(shù)收集
const setupResult = callWithErrorHandling(
setup,
instance,
ErrorCodes.SETUP_FUNCTION,
[__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
)
// currentInstance = null;
unsetCurrentInstance()
}
}
最后在執(zhí)行sharedContext.activate
和sharedContext.deactivate
的時(shí)候?qū)⒆?cè)在實(shí)例上的回調(diào)函數(shù)取出來(lái)直接執(zhí)行就OK了,執(zhí)行時(shí)機(jī)在 postRender 之后
sharedContext.activate = (vnode, container, anchor) => {
// KeepAlive 下組件激活時(shí)執(zhí)行的 move 邏輯
move(vnode, container, anchor, 0 /* ENTER */)
// 把回調(diào)推入到 postFlush 的異步任務(wù)隊(duì)列中去執(zhí)行
queuePostRenderEffect(() => {
if (instance.a) {
// a是 activated 鉤子的簡(jiǎn)稱
invokeArrayFns(instance.a)
}
})
}
sharedContext.activate = (vnode, container, anchor) => {
// KeepAlive 下組件失活時(shí)執(zhí)行的 move 邏輯
move(vnode, container, anchor, 0 /* ENTER */)
queuePostRenderEffect(() => {
if (instance.da) {
// da是 deactivated 鉤子的簡(jiǎn)稱
invokeArrayFns(instance.da)
}
})
}
export const enum LifecycleHooks {
// ... 其他生命周期聲明
DEACTIVATED = 'da',
ACTIVATED = 'a',
}
export interface ComponentInternalInstance {
// ... 其他生命周期
[LifecycleHooks.ACTIVATED]: Function[]
[LifecycleHooks.DEACTIVATED]: Function[]
}
以下是關(guān)于上述demo如何實(shí)現(xiàn)的簡(jiǎn)化流程圖
KeepAlive 組件的onMounted
和onUpdated
生命周期時(shí)進(jìn)行緩存
緩存數(shù)量超過(guò)設(shè)置的 max 時(shí)
監(jiān)聽(tīng) include 和 exclude 修改的時(shí)候,會(huì)讀取緩存中的知進(jìn)行判斷是否需要清除緩存
修剪緩存的時(shí)候也要 unmount(如果該緩存不是當(dāng)前組件)或者 resetShapeFlag 將標(biāo)志為從 KeepAlive 相關(guān) shapeFlag 狀態(tài)重置為 STATEFUL_COMPONENT 狀態(tài)(如果該緩存是當(dāng)前組件,但是被exclude了),當(dāng)然 unmount 函數(shù)內(nèi)包含 resetShapeFlag 操作
KeepAlive 組件的緩存策略是 LRU(last recently used)緩存策略
核心思想在于需要把當(dāng)前訪問(wèn)或渲染的組件作為最新一次渲染的組件,并且該組件在緩存修剪過(guò)程中始終是安全的,即不會(huì)被修剪。
sharedContext.activate = (vnode, container, anchor) => {
// instance 是子組件實(shí)例
const instance = vnode.component!
// ...
// dev環(huán)境下設(shè)置, 自己模擬寫(xiě)的
devtools.emit('component:added', instance.appContext.app, instance.uid, instance.parent ? instance.parent.uid: undefined, instance)
// 官方添加
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
// Update components tree
devtoolsComponentAdded(instance)
}
}
// 同理 sharedContext.deactivates 上也要添加,不然不會(huì)顯示在組件樹(shù)上
當(dāng)子組件有 prop 更新時(shí)是需要重新去 patch 的,所以在 activate 的時(shí)候需要重新執(zhí)行 patch 進(jìn)行子組件更新
sharedContext.activate = (vnode, container, anchor) => {
// ...
// props 改變需要重新 patch(update)
patch(
instance.vnode,
vnode,
container,
anchor,
instance,
parentSuspense,
isSVG,
vnode.slotScopeIds,
optimized
)
}
到此,關(guān)于“Vue中的KeepAlive組件怎么使用”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
分享文章:Vue中的KeepAlive組件怎么使用
轉(zhuǎn)載源于:http://jinyejixie.com/article2/gdpeic.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供自適應(yīng)網(wǎng)站、虛擬主機(jī)、網(wǎng)站維護(hù)、品牌網(wǎng)站建設(shè)、網(wǎng)站策劃、商城網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)