記一次vue 的keep-alive踩坑之路

這個坑,從項目開始的時候就一直存在,在前期的時候還沒有影響到正常的流程,加之項目比較緊,就一直沒有花太多時間去管它。 可是在最近的業(yè)務(wù)中,這個問題已經(jīng)影響到了正常的流程,所以這周一直在尋找這個問題的解決方案。在經(jīng)歷了各種論壇和各種貼子之后,找到了我目前能找到的最合適的解決方法,于是寫下了這篇文章。

背景

用vue+cordova 做一個hybrid App,由于業(yè)務(wù)涉及的輸入數(shù)據(jù)比較多,可能一個表單會包含多個子頁面,這就要求在錄入數(shù)據(jù)的時候,進入子頁面之前需要將當前頁的數(shù)據(jù)緩存,而在子頁面返回的時候,子頁面要銷毀。也就是說,app正常前進的時候,頁面全部緩,返回的時候之前的頁面不需要刷新數(shù)據(jù),直接讀取緩存數(shù)據(jù)。
具體場景和這位仁兄一致: 另辟蹊徑:vue單頁面,多路由,前進刷新,后退不刷新

為了找到一個合適的解決方案,我將這個場景進行了盡可能的簡化,做成了一個DEMO,用來模擬這個場景。

Demo是用Vue-cli生成的,加上了簡單的幾個文件
項目結(jié)構(gòu)如下圖:


項目結(jié)構(gòu)圖

其中,child目錄用來模擬子路由的,first/second是一級路由
具體路由文件如下:


router/index.js

最開始,什么都不處理,只要上頁面上加上<router-link/> 是可以直接跳轉(zhuǎn)的,當然也不會緩存任何組件。

給子路由加上緩存

我的最終目的給項目中所有的模塊進行單獨緩存,而一級頁面不緩存。所以用child來模擬一個模塊,first.vue 和 second.vue來模擬一級頁面
每個模塊(child)都有自己的入口,如child/index.vue,內(nèi)容如下:


child/index.vue

這樣可以將child下面所有的子路由進行緩存,可以用vue-devtool工具進行查看是否能夠成功緩存


vue-devtool

可以看到,child下面的list以及detail組件已經(jīng)緩存成功了。
到這里,已經(jīng)實現(xiàn)了子路由的成功緩存。

但是這樣會有一個問題:在頁面返回的時候,所以被緩存的組件,會一直被緩存,再次進入頁面的時候,組件并不會自動刷新,所以造成從list 點擊不同的詳情頁的時候,進入的都是同一個頁面。
在vue-devtool中看到的現(xiàn)象如下圖:

緩存成功

當然,這種情況下也是有方法進行數(shù)據(jù)刷新的。因為所有被緩存的組件,再次進入的時候,會觸發(fā)其生命周期的activated方法(https://cn.vuejs.org/v2/api/#keep-alive),可以在此進行強制數(shù)據(jù)刷新。
顯然,這種方法不是我想要的。

在返回之前銷毀當前實例

上面那種方法的問題在于:頁面返回的時候,后面的組件(實例)還被緩存著,所以vue并不會重新去刷新它們。那么,如果在頁面返回之前將自己銷毀掉,vue也就找不到了,問題不就解決了么

正好,我項目用的ui框架是vonic https://github.com/wangdahoo/vonic/ ,在學習大神的源碼的時候,發(fā)現(xiàn)了這樣的一段代碼,

vonic

這段代碼的作用有兩點:1、設(shè)置頁面切換的動畫(前進=》'forward',返回=>'back' );2、設(shè)置頁面返回后的page position,也就是頁面滾動的位置。
實現(xiàn)原理大致如下:
用sessionStorage來存儲app打開過的路由,每當在路由跳轉(zhuǎn)之前,先判斷將要去的路由toRoute是不是在sessionStorage里面存在:如果存在,那么頁面就是返回,將下一次路由切換的動畫換成back,并改變sessionStorage中的history=false;如果不存在,那就是前進打開新的頁面,同時將下一次路由切換的動畫改成forward,同時改變sessionStorage中的history=true,并記錄當前頁的scrollTop。

其實vue-router本身的路由和瀏覽器的history對象一樣,對應用程序來說,是不能直接操作的,這里作者用sessionStorage來模擬一個history,從而實現(xiàn)某些功能,這種方法是值得點贊的。

到這里,我們已經(jīng)可以知道應用曾經(jīng)打開過哪些頁面,那我們在返回的時候是不是可以將某些頁面銷毀呢?答案是肯定的。

我們要做的是,在頁面返回之前,也就是路由切換之前,要獲取需要銷毀的實例對象。
上圖中有一個beforeEach,是vue-router提供的一個全局的鉤子函數(shù),它可以監(jiān)聽到每一次路由的切換,但卻獲取不到任何vue的實例對象。
在看了幾遍vue-router的文檔之后https://router.vuejs.org/zh-cn/advanced/navigation-guards.html ,我找到了一個組件內(nèi)的鉤子,也就是官方文檔提到的導航守衛(wèi),如下圖所示:

導航守衛(wèi)

其中,我們要用到的是這個beforeRouteLeave,在將要離開組件對應路由的時候會觸發(fā)這個鉤子。而最重要的是,這個鉤子可以獲取到當前的組件實例this,所以我們可以在這個時候調(diào)用this.$destory()來銷毀當前實例,就能完美解決上面提到的問題。
而這個鉤子只能在組件內(nèi)使用,也就意味著我每個需要緩存的組件都要調(diào)用這個鉤子,難道每個組件里面都去寫一遍這個鉤子函數(shù)嗎?

當然不用。

vue提供了全局的mixin方法,可以幫你在所有的實例里面加上這個鉤子。

由于我的demo中沒有用到vonic,所以我只能參照大神的源碼,寫了一段,具體如下:


main.js

這樣,便可以實現(xiàn),前進的時候緩存組件,返回的時候不刷新,并將最后一個頁面銷毀。

理論上,這個方案挺完美的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文首發(fā)于TalkingCoder,一個有逼格的程序員社區(qū)。轉(zhuǎn)載請注明出處和作者。 寫在前面 本文為系列文章,總共...
    Aresn閱讀 9,686評論 0 42
  • Vue 實例 屬性和方法 每個 Vue 實例都會代理其 data 對象里所有的屬性:var data = { a:...
    云之外閱讀 2,361評論 0 6
  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容,還有我對于 Vue 1.0 印象不深的內(nèi)容。關(guān)于...
    云之外閱讀 5,168評論 0 29
  • 1.安裝 可以簡單地在頁面引入Vue.js作為獨立版本,Vue即被注冊為全局變量,可以在頁面使用了。 如果希望搭建...
    Awey閱讀 11,278評論 4 129
  • 坐在沙發(fā)一頭,右腿蜷在左腿下,胳膊支在扶手上,低矮的木質(zhì)扶手還墊了個抱枕,靠上去很舒服。已經(jīng)快下午三點了,今天你還...
    Lnrsyao閱讀 252評論 0 0

友情鏈接更多精彩內(nèi)容