另闢蹊徑:vue單頁面,多路由,前進刷新,後退不刷新

目的:vue-cli構建的vue單頁面應用,某些特定的頁面,實現前進刷新,後退不刷新,相似app般的用戶體驗。
注: 此處的刷新特指當進入此頁面時,觸發ajax請求,向服務器獲取數據。不刷新特指當進入此頁面時,不觸發ajax請求,而是使用以前緩存的數據,以便減小服務器請求,用戶體驗更流暢。html

項目需求:

任何技術的探索,都來自項目的需求。以前經手的一個項目是微信端商城,使用的是傳統的mvc模式,利用的是jq+js,所以對於商城的項目需求比較熟悉。目前在學習vue,練手一個商城,遇到以前常常說起而沒法很好解決的需求。有些頁面須要前進刷新,後退不刷新。好比,從商城的【首頁】-->【詳情頁】-->【訂單提交頁】,每次打開新頁面都須要獲取新數據,可是按下返回鍵後,就不須要再獲取新數據了,而滾動條還保留在以前的位置。最多見的操做是從【首頁】-->【詳情頁】,而後在從【詳情頁】-->【首頁】,如此反覆。
實例如圖:
首頁詳情頁訂單提交頁vue

前人經驗:

前人栽樹,後人好乘涼。技術圈的分享一直都在蓬勃發展。遇到問題,咱們能夠盡情去搜索,去尋找大佬的足跡。針對上述需求,看到一個分享vue-router 之 keep-alive,比較符合個人需求,可是使用到個人項目上發現,稍微有點不適合。此分享技術要點,比較適合兩個頁面以前的跳轉,返回。而個人頁面是多個路由(2+)之間的跳轉,返回。無奈,只能去本身探索發現。不過此技術要點給了我很好的啓發,特此感謝做者。@ RoamIngit

實現思路:

注:demo中,index頁面包含三個連接導航。page1-->page2-->page3.依次前進,每次前進到一個新頁面都須要獲取數據,而按下後退鍵後,從page3返回到page2,page2再也不獲取新數據,而是使用以前緩存的數據。從page2返回到page1時,page1再也不獲取新數據,而是使用以前的數據。因此,page1和page2須要緩存,page3不須要緩存。能夠把page1想象成首頁,page2想象成詳情頁,page3想象成訂單提交頁。這樣方便理解。github

  • 利用keep-alive 緩存須要緩存的頁面

    • 在app.vue中改寫router-viewajax

      <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中添加路由元信息,設置須要緩存的頁面vue-router

      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, // 此組件不須要被緩存
              }
          }
      ]
    • 鉤子函數的執行順序vue-cli

      • 不使用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;

鉤子函數執行順序

結束語:

爲了解決這個前進刷新後退不刷新問題,讓我整整苦惱了一週時間,想了不少方法,也沒能解決。最後綜合各個大佬經驗,試驗了不少次,才歸結出這個比較‘low’的方法。
目前,我也是vue小白,也在探索着前進,若是這個方法能解決你遇到的難題,我很高興。若是你認爲的確很low,求輕噴。
demo在下方的GitHub中,歡迎star。
也歡迎你們提供意見和建議,謝謝你們

GitHub

相關文章
相關標籤/搜索