關于對keep-alive的理解,使用場景以及存在的問題解讀
結論先行
<keep-alive>是 Vue 的內置組件,主要是用于緩存組件的實例,避免組件重復的被創(chuàng)建和銷毀組件,提高應用的響應速度和性能。
原理:keep-alive 是一個緩存,會標記這個虛擬節(jié)點被緩存過了,后續(xù)就不會重新初始化,也不會進行銷毀。
那常見的配置屬性有 include 、exclude 和 max;
include 和 exclude 用于指定需要緩存或排除的組件名稱;
而 max 是用來定義組件的最大緩存?zhèn)€數。
內部采用 LRU 算法用于維護緩存列表。如果緩存?zhèn)€數超過最大數,那么會將最久沒有被訪問到的組件移出緩存列表,也就是銷毀組件。
keep-alive 提供了兩個鉤子函數: activated 鉤子和 deactivated 鉤子。
- activated:當組件被激活(使用)的時候觸發(fā),常用于執(zhí)行一些數據初始化或者異步操作;
- deactivated:當組件失活(不被使用)的時候觸發(fā);
但是在使用 keep-alive 時,要注意數據的更新問題,避免出現(xiàn)數據無法更新的情況。
一、背景
在平常開發(fā)中,有部分組件沒有必要多次初始化。這時,我們需要將組件進行持久化,使組件的狀態(tài)維持不變。
在下一次展示時,也不會進行重新初始化組件。keep-alive就有這個功能。
二、含義
<keep-alive>是 Vue 的內置組件,可以使被包裹的組件保留狀態(tài),避免重復的創(chuàng)建和銷毀組件,提高應用的響應速度和性能。
當一個組件被<keep-alive>標簽包裹時,會緩存組件的實例在內存中,而不會把組件銷毀。當這個組件再次被使用時,Vue會從緩存中提取組件的實例,將其重新掛載到頁面上。
這個功能可以提高應用的性能,特別是在需要頻繁切換組件的場景下,就比如Tab切換或者路由切換,因為不需要每一次切換時都重新創(chuàng)建和銷毀組件,而是直接從緩存中獲取,這樣可以避免重復的初始化和渲染,從而提高應用的響應速度和性能。
舉個應用場景:
有個員工列表,現(xiàn)在我們點擊某條數據,查看員工詳情后,再返回到員工列表。這個時候我們就希望這個列表能夠保持剛才的狀態(tài),這時候就可以使用keep-alive把這個列表所在的組件包裹。
和 <transition> 相似,<keep-alive> 是一個抽象組件:它自身不會渲染一個 DOM 元素,也不會出現(xiàn)在父組件鏈中。
三、配置屬性
include 和 exclude用于指定需要緩存或排除的組件名稱;
max 和 min 用于指定緩存的最大和最小數量。
① include:包含
值為字符串 / 正則表達式 / 數組。只有組件的名稱(name)與 include 的值相同的才會被緩存。即指定哪些被緩存,可以指定多個被緩存。
這里以字符串為例,指定多個組件緩存,語法是用逗號隔開。
如下:
// 指定 home 組件和 about 組件被緩存 <keep-alive include="home,about"> <router-view></router-view> </keep-alive>
② exclude:排除,優(yōu)先級大于 include
同上,指定哪些組件不被緩存。
// 除了home組件和about組件,別的都緩存 <keep-alive exclude="home,about"> <router-view></router-view> </keep-alive>
使用 include 和 exclude 屬性時,緩存組件的名稱 name 一定要賦值,否則無法識別到對應的路由組件。
如果 name
選項不可用,則匹配它的局部注冊名稱 (父組件 components
選項的鍵值),匿名組件不能被匹配。
當使用正則或者是數組時,要記得使用v-bind 。
<!-- 逗號分隔字符串 --> <keep-alive include="a,b"> <component :is="view"></component> </keep-alive> <!-- 正則表達式 (使用 `v-bind`) --> <keep-alive :include="/a|b/"> <component :is="view"></component> </keep-alive> <!-- 數組 (使用 `v-bind`) --> <keep-alive :include="['a', 'b']"> <component :is="view"></component> </keep-alive>
③ max:定義組件的最大緩存?zhèn)€數
內部采用LRU算法用于維護緩存列表。如果緩存?zhèn)€數超過最大數,那么會將最久沒有被訪問到的組件移出緩存列表,也就是銷毀組件。
四、使用 keep-alive 的鉤子函數執(zhí)行順序問題
keep-alive 提供了兩個鉤子函數: activated 鉤子和 deactivated 鉤子。
- activated:當組件被激活(使用)的時候觸發(fā),即進入這個頁面的時候觸發(fā),常用于執(zhí)行一些數據初始化或者異步操作;
- deactivated:當組件失活(不被使用)的時候觸發(fā),即離開這個頁面的時候觸發(fā);
① 首次進出組件時:
beforeRouteEnter
> beforeCreate
> created
> mounted
> activated
> ... ... > beforeRouteLeave
> deactivated
② 再次進出組件時:
beforeRouteEnter
> activated
> ... ... > beforeRouteLeave
> deactivated
③ 結論
初始進入和離開 created ---> mounted ---> activated --> deactivated
后續(xù)進入和離開 activated --> deactivated
五、應用場景
① 緩存動態(tài)組件
② 緩存路由
Vue2寫法:
Vue3寫法:
在緩存路由的時候,需要借助 v-slot 插槽 + 動態(tài)組件的方式,實現(xiàn)路由的緩存。
也可以通過meta屬性指定哪些頁面需要緩存
③ 實際場景
1)查看表格某條數據的詳情頁,返回還是之前的狀態(tài)。比如還是之前的篩選結果,還是之前的頁數等;
2)填寫的表單的內容路由跳轉返回還在。比如 input框、下選擇拉框、開關切換等用戶輸入了一大把東西,跳轉再回來不能清空啊,不用讓用戶再寫一遍。
六、原理
keep-alive 是一個緩存,會標記這個虛擬節(jié)點被緩存過了。
后續(xù)就不會重新初始化,也不會進行銷毀。
keep-alive
是Vue中內置的一個組件,源碼位置:src/core/components/keep-alive.js
解析:
abstract:true,標記這個組件沒有任何含義,不需要記錄父子關系中。
export default { name: 'keep-alive', abstract: true, props: { include: [String, RegExp, Array], exclude: [String, RegExp, Array], max: [String, Number] }, created () { this.cache = Object.create(null) this.keys = [] }, destroyed () { for (const key in this.cache) { pruneCacheEntry(this.cache, key, this.keys) } }, mounted () { this.$watch('include', val => { pruneCache(this, name => matches(val, name)) }) this.$watch('exclude', val => { pruneCache(this, name => !matches(val, name)) }) }, render() { /* 獲取默認插槽中的第一個組件節(jié)點 */ const slot = this.$slots.default const vnode = getFirstComponentChild(slot) /* 獲取該組件節(jié)點的componentOptions */ const componentOptions = vnode && vnode.componentOptions if (componentOptions) { /* 獲取該組件節(jié)點的名稱,優(yōu)先獲取組件的name字段,如果name不存在則獲取組件的tag */ const name = getComponentName(componentOptions) const { include, exclude } = this /* 如果name不在inlcude中或者存在于exlude中則表示不緩存,直接返回vnode */ if ( (include && (!name || !matches(include, name))) || // excluded (exclude && name && matches(exclude, name)) ) { return vnode } const { cache, keys } = this /* 獲取組件的key值 */ 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 /* 拿到key值后去this.cache對象中去尋找是否有該值,如果有則表示該組件有緩存,即命中緩存 */ if (cache[key]) { vnode.componentInstance = cache[key].componentInstance // make current key freshest remove(keys, key) keys.push(key) } /* 如果沒有命中緩存,則將其設置進緩存 */ else { cache[key] = vnode keys.push(key) // prune oldest entry /* 如果配置了max并且緩存的長度超過了this.max,則從緩存中刪除第一個 */ if (this.max && keys.length > parseInt(this.max)) { pruneCacheEntry(cache, keys[0], keys, this._vnode) } } vnode.data.keepAlive = true } return vnode || (slot && slot[0]) } }
可以看到該組件沒有template
,而是用了render
,在組件渲染的時候會自動執(zhí)行render
函數
this.cache
是一個對象,用來存儲需要緩存的組件,它將以如下形式存儲:
this.cache = { 'key1':'組件1', 'key2':'組件2', // ... }
在組件銷毀的時候執(zhí)行pruneCacheEntry
函數:
function pruneCacheEntry ( cache: VNodeCache, key: string, keys: Array<string>, current?: VNode ) { const cached = cache[key] /* 判斷當前沒有處于被渲染狀態(tài)的組件,將其銷毀*/ if (cached && (!current || cached.tag !== current.tag)) { cached.componentInstance.$destroy() } cache[key] = null remove(keys, key) }
在mounted
鉤子函數中觀測 include
和 exclude
的變化,如下:
mounted () { this.$watch('include', val => { pruneCache(this, name => matches(val, name)) }) this.$watch('exclude', val => { pruneCache(this, name => !matches(val, name)) }) }
如果include
或exclude
發(fā)生了變化,即表示定義需要緩存的組件的規(guī)則或者不需要緩存的組件的規(guī)則發(fā)生了變化,那么就執(zhí)行pruneCache
函數,函數如下:
function pruneCache (keepAliveInstance, filter) { const { cache, keys, _vnode } = keepAliveInstance for (const key in cache) { const cachedNode = cache[key] if (cachedNode) { const name = getComponentName(cachedNode.componentOptions) if (name && !filter(name)) { pruneCacheEntry(cache, key, keys, _vnode) } } } }
在該函數內對this.cache
對象進行遍歷,取出每一項的name
值,用其與新的緩存規(guī)則進行匹配,如果匹配不上,則表示在新的緩存規(guī)則下該組件已經不需要被緩存,則調用pruneCacheEntry
函數將其從this.cache
對象剔除即可。
關于keep-alive
的最強大緩存功能是在render
函數中實現(xiàn),首先獲取組件的key
值:
const key = vnode.key == null? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '') : vnode.key
拿到key
值后去this.cache
對象中去尋找是否有該值,如果有則表示該組件有緩存,即命中緩存,如下:
/* 如果命中緩存,則直接從緩存中拿 vnode 的組件實例 */ if (cache[key]) { vnode.componentInstance = cache[key].componentInstance /* 調整該組件key的順序,將其從原來的地方刪掉并重新放在最后一個 */ remove(keys, key) keys.push(key) }
直接從緩存中拿 vnode
的組件實例,此時重新調整該組件key
的順序,將其從原來的地方刪掉并重新放在this.keys
中最后一個。
this.cache
對象中沒有該key
值的情況,如下:
/* 如果沒有命中緩存,則將其設置進緩存 */ else { cache[key] = vnode keys.push(key) /* 如果配置了max并且緩存的長度超過了this.max,則從緩存中刪除第一個 */ if (this.max && keys.length > parseInt(this.max)) { pruneCacheEntry(cache, keys[0], keys, this._vnode) } }
表明該組件還沒有被緩存過,則以該組件的key
為鍵,組件vnode
為值,將其存入this.cache
中,并且把key
存入this.keys
中。
此時再判斷this.keys
中緩存組件的數量是否超過了設置的最大緩存數量值this.max
,如果超過了,則把第一個緩存組件刪掉。
七、keep-alive存在的問題:緩存后如何獲取數據
存在的問題:數據更新問題,緩存的組件重新進入不會再觸發(fā)created生命周期中的方法,因此數據不會更新。
解決辦法:
① 在 beforeRouteEnter 鉤子函數中,在路由進入之前先獲取數據:
每次組件渲染或者每次進入路由的時候,都會執(zhí)行 beforeRouteEnter
。
beforeRouteEnter(to, from, next){ next(vm=>{ console.log(vm) // 每次進入路由執(zhí)行 vm.getData() // 獲取數據 }) },
② 在 activated 生命周期中,獲取數據:
在 keep-alive
緩存的組件被激活的時候,都會執(zhí)行 actived
鉤子。
activated() { this.getData() // 獲取數據 },
注意:服務器端渲染期間 activated
不被調用。
八、總結
總的來說,使用 keep-alive可以有效地提高應用的響應速度和性能,特別是在需要頻繁切換組件的情況下。
但是在使用 keep-alive 時,要注意數據的更新問題,避免出現(xiàn)數據無法更新的情況。
keep-alive 是一個緩存,會標記這個虛擬節(jié)點被緩存過了,后續(xù)就不會重新初始化,也不會進行銷毀。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
vue+element的表格實現(xiàn)批量刪除功能示例代碼
這篇文章主要介紹了vue+element的表格實現(xiàn)批量刪除功能示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08Vue3 組件間通信之mitt實現(xiàn)任意組件間通信的步驟
mitt 主要有4個API:emit(觸發(fā)某個事件)、on(綁定事件)、off(解綁某個事件)、all(獲取所有綁定的事件),這篇文章主要介紹了Vue3 組件間通信之mitt實現(xiàn)任意組件間通信,需要的朋友可以參考下2024-05-05vue el-select與el-tree實現(xiàn)支持可搜索樹型
本文主要介紹了vue el-select與el-tree實現(xiàn)支持可搜索樹型,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-08-08uniapp前端支付篇之微信、抖音、快手、h5四個平臺支付功能
支付功能在我們日常開發(fā)中經常會遇到,下面這篇文章主要給大家介紹了關于uniapp前端支付篇之微信、抖音、快手、h5四個平臺支付功能的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-03-03