[轉] 2017-11-20 發布 另辟蹊徑:vue單頁面,多路由,前進刷新,後退不刷新
目的:vue-cli構建的vue單頁面應用,某些特定的頁面,實現前進刷新,後退不刷新,類似app般的用戶體驗。
註: 此處的刷新特指當進入此頁面時,觸發ajax請求,向服務器獲取數據。不刷新特指當進入此頁面時,不觸發ajax請求,而是使用之前緩存的數據,以便減少服務器請求,用戶體驗更流暢。
項目需求:
任何技術的探索,都來自項目的需求。之前經手的一個項目是微信端商城,使用的是傳統的mvc模式,利用的是jq+js,因此對於商城的項目需求比較熟悉。目前在學習vue,練手一個商城,遇到之前經常提及而無法很好解決的需求。有些頁面需要前進刷新,後退不刷新。比如,從商城的【首頁】-->【詳情頁】-->【訂單提交頁】,每次打開新頁面都需要獲取新數據,但是按下返回鍵後,就不需要再獲取新數據了,而滾動條還保留在之前的位置。最常見的操作是從【首頁】-->【詳情頁】,然後在從【詳情頁】-->【首頁】,如此反復。
前人經驗:
前人栽樹,後人好乘涼。技術圈的分享一直都在蓬勃發展。遇到問題,我們可以盡情去搜索,去尋找大佬的足跡。針對上述需求,看到一個分享vue-router 之 keep-alive,比較符合我的需求,但是使用到我的項目上發現,稍微有點不適合。此分享技術要點,比較適合兩個頁面之前的跳轉,返回。而我的頁面是多個路由(2+)之間的跳轉,返回。無奈,只能去自己探索發現。不過此技術要點給了我很好的啟發,特此感謝作者。@ RoamIn
實現思路:
註:demo中,index頁面包含三個鏈接導航。page1-->page2-->page3.依次前進,每次前進到一個新頁面都需要獲取數據,而按下後退鍵後,從page3返回到page2,page2不再獲取新數據,而是使用之前緩存的數據。從page2返回到page1時,page1不再獲取新數據,而是使用之前的數據。所以,page1和page2需要緩存,page3不需要緩存。可以把page1想象成首頁,page2想象成詳情頁,page3想象成訂單提交頁。這樣方便理解。
-
利用keep-alive 緩存需要緩存的頁面
-
在app.vue中改寫router-view
<keep-alive> <router-view v-if="$route.meta.keepAlive"> <!-- 這裏是會被緩存的視圖組件,比如 page1,page2 --> </router-view> </keep-alive> <router-view v-if="!$route.meta.keepAlive"> <!-- 這裏是不被緩存的視圖組件,比如 page3 --> </router-view>
-
在router/index.js中添加路由元信息,設置需要緩存的頁面
routes: [{ path: ‘/‘, name: ‘index‘, component: index, meta: { keepAlive: false, //此組件不需要被緩存 } }, { path: ‘/page1‘, name: ‘page1‘, component: page1, meta: { keepAlive: true, //此組件需要被緩存 } }, { path: ‘/page2‘, name: ‘page2‘, component: page2, meta: { keepAlive: true, // 此組件需要被緩存 } }, { path: ‘/page3‘, name: ‘page3‘, component: page3, meta: { keepAlive: false, // 此組件不需要被緩存 } } ]
-
鉤子函數的執行順序
-
不使用keep-alive
beforeRouteEnter --> created --> mounted --> destroyed -
使用keep-alive
beforeRouteEnter --> created --> mounted --> activated --> deactivated
再次進入緩存的頁面,只會觸發beforeRouteEnter -->activated --> deactivated 。created和mounted不會再執行。我們可以利用不同的鉤子函數,做不同的事。務必理解上述鉤子函數的執行時機和執行順序,本教程的核心就依賴於此鉤子函數
activated和deactivated是使用keep-alive後,vue中比較重要的兩個鉤子函數,建議詳細了解下。
-
-
-
需緩存的頁面的寫法
註:demo中的page1和page2,這兩個頁面都需要緩存,思路一樣,以下以page1為例,page2不再贅述。
示例文件:components/page1.vue-
data中初始化一個str字符串,存放從後臺獲取的數據
data() { return { msg: "我是第一個頁面", str: "" // 加載頁面後執行獲取數據的方法,插入到此 }; }
-
methods中創建一個方法,模擬從後臺獲取數據
methods: { getData() { // getData方法,模擬從後臺請求數據 this.str = "我是通過調用方法加載的數據。。。"; } }
-
修改router/index.js中的配置
-
每次進入頁面,我們都需要知曉是從哪個頁面進來的,用以判斷是否需要獲取數據。以這個page1頁面為例,當我們知曉是從page2過來的,我們就可以認為是用戶操作了返回鍵,這時page1頁面就不需要再獲取新數據了,使用之前緩存的數據就可以了。如果是從別的頁面過來的,我們就需要獲取數據。
-
我們可以通過beforeRouteEnter這個鉤子函數中的from參數判斷是從哪個頁面過來的,這個參數執行時,組件實例還沒創建,所有不能在data中定義變量。我們可以在路由中定義一個變量,用來判斷。
-
在router/index.js的meta中添加isBack變量,默認false
{ path: ‘/page1‘, name: ‘page1‘, component: page1, meta: { keepAlive: true, //此組件需要被緩存 isBack:false, //用於判斷上一個頁面是哪個 } }, { path: ‘/page2‘, name: ‘page2‘, component: page2, meta: { keepAlive: true, // 此組件需要被緩存 isBack:false, //用於判斷上一個頁面是哪個 } },
-
-
beforeRouteEnter中判斷是從哪個頁面過來的
-
判斷是從哪個路由過來的,如果是page2過來的,表明當前頁面不需要刷新獲取新數據,直接用之前緩存的數據即可
beforeRouteEnter(to, from, next) { // 路由導航鉤子,此時還不能獲取組件實例 `this`,所以無法在data中定義變量(利用vm除外) // 參考 https://router.vuejs.org/zh-cn/advanced/navigation-guards.html // 所以,利用路由元信息中的meta字段設置變量,方便在各個位置獲取。這就是為什麽在meta中定義isBack // 參考 https://router.vuejs.org/zh-cn/advanced/meta.html if(from.name==‘page2‘){ to.meta.isBack=true; //判斷是從哪個路由過來的, //如果是page2過來的,表明當前頁面不需要刷新獲取新數據,直接用之前緩存的數據即可 } next(); },
-
-
activated中執行getData這個獲取數據的方法
-
因為這個頁面需要緩存。只有第一次進入時才會執行created和mounted方法,再次進入就不執行了。而activated每次進入都執行,所以在這個鉤子函數中獲取數據。
activated() { if(!this.$route.meta.isBack){ // 如果isBack是false,表明需要獲取新數據,否則就不再請求,直接使用緩存的數據 this.getData(); } // 恢復成默認的false,避免isBack一直是true,導致下次無法獲取數據 this.$route.meta.isBack=false },
-
-
這樣就可以了?
-
當這樣設置完畢後,你執行起來,貌似是可以了。第一次進入page1,能獲取新數據,從page2返回時,不再獲取新數據了,而是使用之前緩存的數據。但這樣還有一個問題,當用戶從page1進入page2後,因為某種原因,手動刷新了page2的頁面。這時再返回到page1,發現之前緩存的數據丟失了,而且也沒有再重新獲取。所以我們還需要再添加一個判斷條件,當用戶手動刷新頁面後,再返回時就需要重新獲取數據了。
-
如何添加這個條件,判斷用戶是否刷新了頁面呢?我們知道,當使用keep-alive後,只有第一次進入後會觸發created鉤子函數,再次進入就不再執行了。當用戶刷新了頁面,這個鉤子函數就會又執行,所以,我們可以利用這個小技巧來做點文章。
-
data中定義變量isFirstEnter用來判斷是否第一次進入,或是否刷新了頁面,默認false
data() { return { msg: "我是第一個頁面", str: "", // 加載頁面後執行獲取數據的方法,插入到此 isFirstEnter:false // 是否第一次進入,默認false }; },
-
created中把isFirstEnter變為true,說明是第一次進入或刷新了頁面
created() { this.isFirstEnter=true; // 只有第一次進入或者刷新頁面後才會執行此鉤子函數 // 使用keep-alive後(2+次)進入不會再執行此鉤子函數 },
-
activated中增加判斷條件
activated() { if(!this.$route.meta.isBack || this.isFirstEnter){ // 如果isBack是false,表明需要獲取新數據,否則就不再請求,直接使用緩存的數據 // 如果isFirstEnter是true,表明是第一次進入此頁面或用戶刷新了頁面,需獲取新數據 this.str=‘‘// 把數據清空,可以稍微避免讓用戶看到之前緩存的數據 this.getData(); } // 恢復成默認的false,避免isBack一直是true,導致下次無法獲取數據 this.$route.meta.isBack=false // 恢復成默認的false,避免isBack一直是true,導致每次都獲取新數據 this.isFirstEnter=false; },
-
這樣應該就可以了
-
-
-
不需要緩存頁面的寫法
註:demo中的page3,這個頁面不需要緩存,該怎麽寫就怎麽寫,不需要做特別的設置。
其它設置:
使用keep-alive後,可能有點小問題:第二個頁面可能繼承第一個頁面的滾動條的高度。(在我項目中遇到的)
比如:page1向下滾動後,再進入page2,這時page2的滾動條可能是之前的高度,可能不會在頂部。
-
解決方法一
每次離開記錄滾動條的高度,再次進入時根據項目需要再恢復之前的高度,或者置頂。 -
解決方法二(推薦)
router/index.js中添加如下代碼(如不理解,請看參考鏈接)
參考:HTML5 History 模式 滾動行為mode: ‘history‘, scrollBehavior(to, from, savedPosition) { if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } }
疑問點:
在此次demo練習中,打印了一下鉤子函數的執行順序,發現一個疑問點(我對鉤子函數理解也很淺顯):
從page1進入page2時,先執行了page2的beforeRouteEnter和created方法,然後才執行page1的deactivated方法。
所以我把這兩個初始化設置,放在了activated裏面,而沒有放在deactivated中
this.$route.meta.isBack=false;
this.isFirstEnter=false;
[轉] 2017-11-20 發布 另辟蹊徑:vue單頁面,多路由,前進刷新,後退不刷新