閱讀本文之前,預設大家對 vue 和 keep-alive 都很熟悉,所以不再囉嗦相關資料,直接進入正文。
有耐心的話,且聽我細細道來,如果你遇到過類似問題,或正在尋找解決方案,那麼你可以直接翻到文末看結論。
本篇為系列篇1:Vue 全站快取之 keep-alive : 動態移除快取
系列篇2:Vue 全站快取二:如何設計全站快取
前言
以一個記賬專案舉例,常見的場景有首頁、記到賬頁面、選擇合同、新建合同、選擇客戶、新建客戶
這些頁面。
在這些頁面中,很顯然,使用者的瀏覽行為應該是逐漸深入的,通俗得講就是瀏覽頁面在不斷前進。
而且這些頁面之間還是有互動性存在的,兩種互動行為:
- 一. 使用者前進時,總是進入新的頁面。(比如在合同列表頁反覆載入多次列表之後,進入其中一個合同詳情,再返回時,應該仍停留之前裡列表頁同一個位置,而不是重新重新整理列表頁。)
- 二. 使用者後退時,需要能保留前一頁資料並繼續操作。(比如,記到賬時需要選擇合同,選擇合同時可以新建合同,新建合同時填了一堆資料可以去選擇客戶,在選擇客戶時又去建立了客戶,那麼這一堆操作下來應該能夠做到:
建立完客戶後繼續新建合同,建完合同後繼續記該合同的到賬
)
上圖是 demo 專案中的真實效果,目前常見的 vue 開發方案裡,一般都會引入 vuex 或 localStorage ,在各個頁面不斷的儲存和呼叫頁面內的資料,我覺得,這很不科學很不優雅。
keep-alive 什麼問題
vue 支援 keep-alive 元件,如果啟用,頁面內的所有資料都會被保留,所以,上文的互動行為二後退時保留前一頁資料繼續操作
沒有問題。
問題出在互動行為一使用者前進時總是進入新頁面
,然而一旦快取,你就沒法總是進新頁面了,你總是進入快取頁,這就很讓人頭疼了。
官方提供了include
和exclude
特性,說你可以決定哪些頁面使用快取哪些頁面不用快取。連結
然而問題又回到了原點,並沒有解決我們酌情決定是否使用已快取的快取
這一需求。
所以很多人想到了一個方法在離開頁面時銷燬這個頁面
是不是就可以了,然而並不能,這裡出現了 bug ,元件銷燬了快取還在:
於是,就有人提出希望keep-alive能增加可以動態刪除已快取元件的功能
,issue
這是個老話題,之前一直沒有進展,核心原因就在於 keep-alive 不能正確處理已銷燬的元件。
嘗試解決這個問題
如果能實現動態使用快取
這一功能,那麼所有問題也就迎刃而解。
最初,我研究 keep-alive 的程式碼,發現了這麼一段程式碼:
於是,我想,如果在此處判斷如果元件已被銷燬則不使用快取
,是不是就解決這個問題了,於是我提交了一個 PR:
不過這個 PR 遲遲沒有通過,我就放棄了。
暴力解決這個問題
我繼續研究有沒有其他方案,然後我在列印元件變數的時候,發現了這麼個眼熟的欄位:
這不就是 keep-alive 的元件嘛,我趕忙點開再看,發現了更眼熟的東東:
於是,這事兒就變得簡單了,直接按圖索驥,我們在銷燬元件之前,尋找路由元件所在父級的 keep-alive 元件,操控其中的 cache 列表,強行刪除其中的快取,問題也就迎刃而解,是不是很直接很暴力。
結論
keep-alive 預設不支援動態銷燬已快取的元件,所以此處給出的解決方案是通過直接操控 keep-alvie 元件裡的 cahce 列表,暴力移除快取:
//使用Vue.mixin的方法攔截了路由離開事件,並在該攔截方法中實現了銷燬頁面快取的功能。
Vue.mixin({
beforeRouteLeave:function(to, from, next){
if (from && from.meta.rank && to.meta.rank && from.meta.rank>to.meta.rank)
{//此處判斷是如果返回上一層,你可以根據自己的業務更改此處的判斷邏輯,酌情決定是否摧毀本層快取。
if (this.$vnode && this.$vnode.data.keepAlive)
{
if (this.$vnode.parent && this.$vnode.parent.componentInstance && this.$vnode.parent.componentInstance.cache)
{
if (this.$vnode.componentOptions)
{
var key = this.$vnode.key == null
? this.$vnode.componentOptions.Ctor.cid + (this.$vnode.componentOptions.tag ? `::${this.$vnode.componentOptions.tag}` : '')
: this.$vnode.key;
var cache = this.$vnode.parent.componentInstance.cache;
var keys = this.$vnode.parent.componentInstance.keys;
if (cache[key])
{
if (keys.length) {
var index = keys.indexOf(key);
if (index > -1) {
keys.splice(index, 1);
}
}
delete cache[key];
}
}
}
}
this.$destroy();
}
next();
},
});
複製程式碼
後語
本文主要圍繞如何動態刪除 keep-alive 快取這一問題進行探索,其他關於如何設定頁面層級、如何在前後頁之間進行資料傳遞
等問題,敬請期待《Vue 全站快取之 vue-router-then :前後頁資料傳遞》。
請繼續閱讀-系列篇2:Vue 全站快取二:如何設計全站快取
原文來自阿星的部落格:wanyaxing.com/blog/201807…