這個坑,從項目開始的時候就一直存在,在前期的時候還沒有影響到正常的流程,加之項目比較緊,就一直沒有花太多時間去管它。 可是在最近的業(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)如下圖:

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

最開始,什么都不處理,只要上頁面上加上<router-link/> 是可以直接跳轉(zhuǎn)的,當然也不會緩存任何組件。
給子路由加上緩存
我的最終目的給項目中所有的模塊進行單獨緩存,而一級頁面不緩存。所以用child來模擬一個模塊,first.vue 和 second.vue來模擬一級頁面
每個模塊(child)都有自己的入口,如child/index.vue,內(nèi)容如下:

這樣可以將child下面所有的子路由進行緩存,可以用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)了這樣的一段代碼,

這段代碼的作用有兩點: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),如下圖所示:

其中,我們要用到的是這個beforeRouteLeave,在將要離開組件對應路由的時候會觸發(fā)這個鉤子。而最重要的是,這個鉤子可以獲取到當前的組件實例this,所以我們可以在這個時候調(diào)用this.$destory()來銷毀當前實例,就能完美解決上面提到的問題。
而這個鉤子只能在組件內(nèi)使用,也就意味著我每個需要緩存的組件都要調(diào)用這個鉤子,難道每個組件里面都去寫一遍這個鉤子函數(shù)嗎?
當然不用。
vue提供了全局的mixin方法,可以幫你在所有的實例里面加上這個鉤子。
由于我的demo中沒有用到vonic,所以我只能參照大神的源碼,寫了一段,具體如下:

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