keep-alive組件的作用與原理分析
什么是keep-alive
“keep-alive” 是 Vue.js 中的一個特殊組件,用于緩存組件的狀態(tài),以提高應用性能。
在 Vue.js 中,組件通常是動態(tài)創(chuàng)建和銷毀的,當切換到另一個頁面或組件時,之前的組件會被銷毀,再次進入時會重新創(chuàng)建和初始化。
這樣可能導致組件的狀態(tài)丟失,需要重新初始化,增加了資源的消耗。
組件解決了這個問題,它可以將組件緩存起來,而不是銷毀,使得組件在再次進入時保持之前的狀態(tài),以及避免重復的創(chuàng)建和初始化過程。
這樣可以大幅度提高組件的加載速度和性能。
keep-alive的作用
<keep-alive>
的作用是在 Vue.js 應用中緩存組件的狀態(tài),以提高應用性能和用戶體驗。
它可以將組件暫時保存在內(nèi)存中,而不是每次都重新創(chuàng)建和初始化組件。
主要的作用有以下幾點:
- 組件狀態(tài)保持:通過使用
<keep-alive>
,在組件被切換時,其狀態(tài)會被保留。這意味著組件內(nèi)部的數(shù)據(jù)、狀態(tài)以及一些計算結果都會被緩存,不會因為組件的銷毀而丟失。當再次進入該組件時,它會恢復到之前的狀態(tài),而不需要重新初始化。這對于用戶在不同頁面或組件間切換時提供了更流暢的體驗。 - 減少資源消耗:如果沒有使用
<keep-alive>
,每次切換到一個組件時,都需要重新創(chuàng)建和初始化組件。對于復雜的組件,這可能會導致不必要的資源消耗,例如重新加載數(shù)據(jù)、執(zhí)行復雜的計算等。而使用<keep-alive>
,組件被緩存起來,下次再次進入時直接從緩存中恢復,避免了重復的初始化過程,大大減少了資源消耗。 - 優(yōu)化性能:由于避免了重復的創(chuàng)建和初始化過程,使用
<keep-alive>
可以顯著提高組件的加載速度,加快頁面響應時間,從而提供更好的用戶體驗。
需要注意的是,<keep-alive>
并不是適用于所有組件的,特別是對于一些動態(tài)變化的組件,如果希望每次進入時都重新初始化,或者希望釋放組件占用的資源,就不應該使用 <keep-alive>
。
要使用 <keep-alive>
,只需將需要緩存的組件包裹在 <keep-alive>
標簽中即可,Vue.js 會自動管理緩存和組件的生命周期。這是一個簡單但強大的功能,可在合適的場景下大幅度提升應用性能。
原理
<keep-alive>
的原理主要涉及兩個方面:組件緩存和生命周期的管理。
組件緩存
- 當一個組件被包裹在 <keep-alive> 標簽中時,Vue.js 會將該組件的實例緩存起來,而不是銷毀它。
- 組件的緩存是通過一個名為 cache 的對象來管理的,該對象會保存被緩存的組件實例。
- 當切換到一個被緩存的組件時,Vue.js 首先檢查 cache 對象中是否已經(jīng)有該組件的緩存實例。如果有,就直接從緩存中取出該實例;如果沒有,就創(chuàng)建一個新的組件實例并將其緩存起來。
生命周期的管理
- 在切換到一個被緩存的組件時,組件的生命周期鉤子函數(shù)并不會被觸發(fā),而是會觸發(fā) <keep-alive> 自己的生命周期鉤子函數(shù)。
- <keep-alive> 組件有兩個主要的生命周期鉤子函數(shù):created 和 destroyed。
- 在組件第一次被緩存時,created 鉤子函數(shù)會被觸發(fā),表示 <keep-alive> 組件已經(jīng)創(chuàng)建,此時會創(chuàng)建被緩存組件的實例并將其緩存起來。
- 在切換到其他組件時,destroyed 鉤子函數(shù)會被觸發(fā),表示 <keep-alive> 組件將被銷毀,此時會銷毀所有緩存的組件實例。
需要注意的是,被包裹在 <keep-alive>
標簽中的組件,必須具有唯一的標識,否則會導致緩存沖突。
默認情況下,Vue.js 使用組件的名稱作為緩存的標識,但也可以通過 key
屬性來指定唯一的標識。
使用 <keep-alive>
時,要注意以下幾點:
- 不是所有組件都適合使用 <keep-alive>,對于一些動態(tài)變化的組件,或者需要每次進入時重新初始化的組件,應該避免使用 <keep-alive>。
- 緩存的組件仍然會觸發(fā) activated 和 deactivated 生命周期鉤子函數(shù),可以在這兩個鉤子函數(shù)中處理一些特定的邏輯。
- 如果被緩存的組件包含了一些依賴于外部狀態(tài)(如路由參數(shù)、Vuex 狀態(tài)等)的邏輯,需要特別注意在重新進入組件時是否需要重新更新這些狀態(tài)。
總的來說,<keep-alive>
提供了一種簡單且強大的機制來優(yōu)化 Vue.js 應用的性能,特別是在頻繁切換組件的場景下。
使用
當您使用 <keep-alive>
組件時,通常需要將需要緩存的組件包裹在 <keep-alive>
標簽中,并為每個被緩存的組件設置一個唯一的 key
屬性,以確保緩存的正確性。
下面是一個使用 <keep-alive>
組件的示例:
假設我們有兩個組件,一個是用于顯示用戶信息的組件 <UserProfile>
,另一個是用于顯示用戶訂單信息的組件 <UserOrders>
。
我們希望在用戶切換到 <UserProfile>
組件時,保持該組件的狀態(tài),并且在用戶切換到 <UserOrders>
組件后再切換回來時,不重新初始化 <UserProfile>
組件。
<template> <div> <keep-alive> <!-- 使用 key 屬性確保組件的正確緩存 --> <component :is="currentComponent" :key="currentComponent" /> </keep-alive> <button @click="showUserProfile">Show User Profile</button> <button @click="showUserOrders">Show User Orders</button> </div> </template> <script> import UserProfile from './UserProfile.vue'; import UserOrders from './UserOrders.vue'; export default { components: { UserProfile, UserOrders, }, data() { return { currentComponent: 'UserProfile', // 初始顯示用戶信息組件 }; }, methods: { showUserProfile() { this.currentComponent = 'UserProfile'; }, showUserOrders() { this.currentComponent = 'UserOrders'; }, }, }; </script>
在上面的示例中,使用了動態(tài)組件 <component :is="currentComponent">
來動態(tài)地切換顯示 <UserProfile>
和 <UserOrders>
組件。
同時,將 <keep-alive>
標簽包裹在動態(tài)組件外部,這樣 <keep-alive>
會緩存當前被顯示的組件。
在切換組件時,使用 key
屬性來確保緩存的正確性。當切換到不同的組件時,key
的值會變化,這會觸發(fā) <keep-alive>
的重新緩存行為。
注意,key
屬性應該是唯一的,以確保每個組件都能被正確地緩存。在實際應用中,可能需要根據(jù)組件的具體情況設置不同的 key
值。
理解源碼
<keep-alive>
組件的源碼相對比較復雜,涉及到 Vue.js 的虛擬 DOM、組件實例管理、生命周期管理等方面。
下面簡要介紹 <keep-alive>
的關鍵源碼部分,以便了解其基本原理。
在 Vue.js 的源碼中,<keep-alive>
組件是由一個特殊的內(nèi)置組件 KeepAlive
實現(xiàn)的。它的主要作用是處理組件的緩存和管理緩存組件的生命周期。
組件的緩存實現(xiàn)
KeepAlive
組件內(nèi)部維護了一個名為cache
的對象,用于存儲緩存的組件實例。- 在切換到一個被緩存的組件時,
KeepAlive
組件首先會檢查cache
對象,是否已經(jīng)有該組件的緩存實例。 - 如果緩存中有該組件實例,
KeepAlive
直接返回緩存的組件實例;如果沒有,KeepAlive
會創(chuàng)建一個新的組件實例,并將其緩存起來。
組件生命周期的管理
KeepAlive
組件有兩個重要的生命周期鉤子函數(shù):created
和destroyed
。- 在
created
鉤子函數(shù)中,KeepAlive
會監(jiān)聽父組件的include
和exclude
屬性的變化,以決定是否緩存某個組件。 - 在切換到被緩存組件時,
KeepAlive
會觸發(fā)activated
生命周期鉤子函數(shù),并從cache
中取出對應的緩存組件實例。如果沒有緩存實例,會觸發(fā)被緩存組件的created
生命周期。 - 在切換到其他組件時,
KeepAlive
會觸發(fā)deactivated
生命周期鉤子函數(shù),并將當前緩存的組件實例暫時從cache
中移除。如果需要緩存,則緩存的組件實例并不會被銷毀。
組件銷毀時的處理
- 在
destroyed
鉤子函數(shù)中,KeepAlive
會銷毀所有緩存的組件實例,并清空cache
對象。
如果想深入了解 <keep-alive>
的源碼實現(xiàn),可以查閱 Vue.js 的 GitHub 倉庫并瀏覽相關代碼 keep-alive源碼。
這個是從github拿的源碼,有興趣可以研究一下。
import { isRegExp, isArray, remove } from 'shared/util' import { getFirstComponentChild } from 'core/vdom/helpers/index' import type VNode from 'core/vdom/vnode' import type { VNodeComponentOptions } from 'types/vnode' import type { Component } from 'types/component' import { getComponentName } from '../vdom/create-component' type CacheEntry = { name?: string tag?: string componentInstance?: Component } type CacheEntryMap = Record<string, CacheEntry | null> function _getComponentName(opts?: VNodeComponentOptions): string | null { return opts && (getComponentName(opts.Ctor.options as any) || opts.tag) } function matches( pattern: string | RegExp | Array<string>, name: string ): boolean { if (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: { cache: CacheEntryMap; keys: string[]; _vnode: VNode }, filter: Function ) { const { cache, keys, _vnode } = keepAliveInstance for (const key in cache) { const entry = cache[key] if (entry) { const name = entry.name if (name && !filter(name)) { pruneCacheEntry(cache, key, keys, _vnode) } } } } function pruneCacheEntry( cache: CacheEntryMap, key: string, keys: Array<string>, current?: VNode ) { const entry = cache[key] if (entry && (!current || entry.tag !== current.tag)) { // @ts-expect-error can be undefined entry.componentInstance.$destroy() } cache[key] = null remove(keys, key) } const patternTypes: Array<Function> = [String, RegExp, Array] // TODO defineComponent export default { name: 'keep-alive', abstract: true, props: { include: patternTypes, exclude: patternTypes, max: [String, Number] }, methods: { cacheVNode() { const { cache, keys, vnodeToCache, keyToCache } = this if (vnodeToCache) { const { tag, componentInstance, componentOptions } = vnodeToCache cache[keyToCache] = { name: _getComponentName(componentOptions), tag, componentInstance } keys.push(keyToCache) // prune oldest entry if (this.max && keys.length > parseInt(this.max)) { pruneCacheEntry(cache, keys[0], keys, this._vnode) } this.vnodeToCache = null } } }, created() { this.cache = Object.create(null) this.keys = [] }, destroyed() { for (const key in this.cache) { pruneCacheEntry(this.cache, key, this.keys) } }, mounted() { this.cacheVNode() this.$watch('include', val => { pruneCache(this, name => matches(val, name)) }) this.$watch('exclude', val => { pruneCache(this, name => !matches(val, name)) }) }, updated() { this.cacheVNode() }, render() { const slot = this.$slots.default const vnode = getFirstComponentChild(slot) const componentOptions = vnode && vnode.componentOptions if (componentOptions) { // check pattern const name = _getComponentName(componentOptions) const { include, exclude } = this if ( // not included (include && (!name || !matches(include, name))) || // excluded (exclude && name && matches(exclude, name)) ) { return vnode } const { cache, keys } = this const key = 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 if (cache[key]) { vnode.componentInstance = cache[key].componentInstance // make current key freshest remove(keys, key) keys.push(key) } else { // delay setting the cache until update this.vnodeToCache = vnode this.keyToCache = key } // @ts-expect-error can vnode.data can be undefined vnode.data.keepAlive = true } return vnode || (slot && slot[0]) } }
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Vue + better-scroll 實現(xiàn)移動端字母索引導航功能
better-scroll 是一款重點解決移動端(已支持 PC)各種滾動場景需求的插件。這篇文章主要介紹了Vue + better-scroll 實現(xiàn)移動端字母索引導航功能,需要的朋友可以參考下2018-05-05Vue transition實現(xiàn)點贊動畫效果的示例
點贊動畫是網(wǎng)頁評論中常見的功能,本文將介紹如何用vue實現(xiàn)這一效果。點贊時愛心縮小變大,變大時略微大一點再變正常,取消點贊時愛心無動畫,同時數(shù)字滾動,+1 時向上滾動,-1 時向下滾動2021-05-05Element el-checkbox-group v-model不支持對象(object)解決方案
本文主要介紹了Element el-checkbox-group v-model不支持對象(object)解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-05-05vue中window.addEventListener(‘scroll‘,?xx)失效的解決
這篇文章主要介紹了vue中window.addEventListener(‘scroll‘,?xx)失效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07