Vue 全站快取之 keep-alive : 動態移除快取

wanyaxing發表於2018-08-01

閱讀本文之前,預設大家對 vue 和 keep-alive 都很熟悉,所以不再囉嗦相關資料,直接進入正文。

有耐心的話,且聽我細細道來,如果你遇到過類似問題,或正在尋找解決方案,那麼你可以直接翻到文末看結論。

本篇為系列篇1:Vue 全站快取之 keep-alive : 動態移除快取

系列篇2:Vue 全站快取二:如何設計全站快取

系列篇3:Vue 全站快取之 vue-router-then :前後頁資料傳遞

系列篇4:Vue 全站快取之 vue-router-then :實現原理

前言

以一個記賬專案舉例,常見的場景有首頁、記到賬頁面、選擇合同、新建合同、選擇客戶、新建客戶這些頁面。

Vue 全站快取之 keep-alive : 動態移除快取

在這些頁面中,很顯然,使用者的瀏覽行為應該是逐漸深入的,通俗得講就是瀏覽頁面在不斷前進。

而且這些頁面之間還是有互動性存在的,兩種互動行為:

  • 一. 使用者前進時,總是進入新的頁面。(比如在合同列表頁反覆載入多次列表之後,進入其中一個合同詳情,再返回時,應該仍停留之前裡列表頁同一個位置,而不是重新重新整理列表頁。)
  • 二. 使用者後退時,需要能保留前一頁資料並繼續操作。(比如,記到賬時需要選擇合同,選擇合同時可以新建合同,新建合同時填了一堆資料可以去選擇客戶,在選擇客戶時又去建立了客戶,那麼這一堆操作下來應該能夠做到:建立完客戶後繼續新建合同,建完合同後繼續記該合同的到賬

Vue 全站快取之 keep-alive : 動態移除快取

上圖是 demo 專案中的真實效果,目前常見的 vue 開發方案裡,一般都會引入 vuex 或 localStorage ,在各個頁面不斷的儲存和呼叫頁面內的資料,我覺得,這很不科學很不優雅

keep-alive 什麼問題

vue 支援 keep-alive 元件,如果啟用,頁面內的所有資料都會被保留,所以,上文的互動行為二後退時保留前一頁資料繼續操作沒有問題。

問題出在互動行為一使用者前進時總是進入新頁面,然而一旦快取,你就沒法總是進新頁面了,你總是進入快取頁,這就很讓人頭疼了。

官方提供了includeexclude特性,說你可以決定哪些頁面使用快取哪些頁面不用快取。連結

然而問題又回到了原點,並沒有解決我們酌情決定是否使用已快取的快取這一需求。

所以很多人想到了一個方法在離開頁面時銷燬這個頁面是不是就可以了,然而並不能,這裡出現了 bug ,元件銷燬了快取還在:

Vue 全站快取之 keep-alive : 動態移除快取

於是,就有人提出希望keep-alive能增加可以動態刪除已快取元件的功能issue

這是個老話題,之前一直沒有進展,核心原因就在於 keep-alive 不能正確處理已銷燬的元件。

嘗試解決這個問題

如果能實現動態使用快取這一功能,那麼所有問題也就迎刃而解。

最初,我研究 keep-alive 的程式碼,發現了這麼一段程式碼:

Vue 全站快取之 keep-alive : 動態移除快取

於是,我想,如果在此處判斷如果元件已被銷燬則不使用快取,是不是就解決這個問題了,於是我提交了一個 PR:

Vue 全站快取之 keep-alive : 動態移除快取

不過這個 PR 遲遲沒有通過,我就放棄了。

暴力解決這個問題

我繼續研究有沒有其他方案,然後我在列印元件變數的時候,發現了這麼個眼熟的欄位:

Vue 全站快取之 keep-alive : 動態移除快取

這不就是 keep-alive 的元件嘛,我趕忙點開再看,發現了更眼熟的東東:

Vue 全站快取之 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…

相關文章