這個坑,從項目開始的時候就一直存在乏苦,在前期的時候還沒有影響到正常的流程,加之項目比較緊尤筐,就一直沒有花太多時間去管它汇荐。 可是在最近的業(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/ ,在學(xué)習(xí)大神的源碼的時候,發(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對象一樣盐类,對應(yīng)用程序來說寞奸,是不能直接操作的,這里作者用sessionStorage來模擬一個history在跳,從而實現(xiàn)某些功能枪萄,這種方法是值得點贊的。
到這里猫妙,我們已經(jīng)可以知道應(yīng)用曾經(jīng)打開過哪些頁面瓷翻,那我們在返回的時候是不是可以將某些頁面銷毀呢?答案是肯定的割坠。
我們要做的是齐帚,在頁面返回之前,也就是路由切換之前彼哼,要獲取需要銷毀的實例對象对妄。
上圖中有一個beforeEach,是vue-router提供的一個全局的鉤子函數(shù)敢朱,它可以監(jiān)聽到每一次路由的切換剪菱,但卻獲取不到任何vue的實例對象。
在看了幾遍vue-router的文檔之后https://router.vuejs.org/zh-cn/advanced/navigation-guards.html 拴签,我找到了一個組件內(nèi)的鉤子孝常,也就是官方文檔提到的導(dǎo)航守衛(wèi),如下圖所示:
其中蚓哩,我們要用到的是這個beforeRouteLeave构灸,在將要離開組件對應(yīng)路由的時候會觸發(fā)這個鉤子。而最重要的是杖剪,這個鉤子可以獲取到當前的組件實例this冻押,所以我們可以在這個時候調(diào)用this.$destory()來銷毀當前實例驰贷,就能完美解決上面提到的問題。
而這個鉤子只能在組件內(nèi)使用洛巢,也就意味著我每個需要緩存的組件都要調(diào)用這個鉤子括袒,難道每個組件里面都去寫一遍這個鉤子函數(shù)嗎?
當然不用稿茉。
vue提供了全局的mixin方法锹锰,可以幫你在所有的實例里面加上這個鉤子。
由于我的demo中沒有用到vonic漓库,所以我只能參照大神的源碼恃慧,寫了一段,具體如下:
這樣渺蒿,便可以實現(xiàn)痢士,前進的時候緩存組件,返回的時候不刷新茂装,并將最后一個頁面銷毀怠蹂。
理論上,這個方案挺完美的少态。