單頁面下模擬路由跳轉

項目需求:一個路由頁面下展示多個頁面,且切換時要像真正的路由一樣可以攜帶路由參數

三個前置知識:

  1. vue不允許在路由棧中推入同一URL地址,否則會告警:NavigationDuplicated: Avoided redundant navigation to current location: "/route".
  1. 在以react、vue爲代表的SPA項目中,在萬物皆組件的今天,所謂路由的切換其實僅是不同組件的切換而已。(寫到這裏,不由得想到一句話:我們從html學到了js再學到以vue/react爲代表的生態框架,本質上也不過在一個div裏打打鬧鬧而已,這個div叫#app...)
  1. 路由間跳轉時如果有參且參數是query類型時,在刷新時將不會丟失參數

基於項目需求以及前置知識:

  1. 畫出了思維導圖
  2. 設計基礎邏輯
// page.vue
<template>
  <div>
    <p1 v-if="currentNode === 1"/>
    <p2 v-if="currentNode  === 2"/>
    <p3 v-if="currentNode === 3"/>
  </div>
</template>
<script>
import p1 from './p1.vue'
import p2 from './p2.vue'
import p3 from './p3.vue'
export default {
  components: {
    p1,
    p2,
    p3
  },
  data() {
   return {
     currentNode : 1   // 當前展示組件
   }
  }

}
</script>

一:修復重複告警問題:
爲了規避前置1產生的問題,重寫了routerInstance方法

// ./router/index.js
// 重寫路由push方法以規避`NavigationDuplicated: Avoided redundant navigation to current location: "/route".`告警
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch((err) => err);
};

二:設計組件切換邏輯

這裏其實想了很多種方法,原先想使用window.location.href + query的方式,但是由於此方法會導致頁面白屏而放棄,後來又嘗試在每個子組件中emit對應的值來使當前組件動態切換, 但總覺得不夠優雅,遂將其作爲兜底方案,直到後來想到了終極解決方案後便放棄了此方案

終極方案便是利用watch+routerQuery。
即:
index頁監聽routeQuery對象,各個子組件內像使用路由跳轉一樣跳轉(僅支持push),而watch存在的目的在於通過監聽query的參數以進行組件間的切換

  • 淺灰標識跳轉至,深灰標識回退至

貼貼:

// page.vue
  watch: {
    // 模擬路由跳轉 -》`routerInstance`重寫了router的`push`方法,因而不會告警
    // 通過監聽路由參數進行組件間的切換 子組件可以直接使用this.$router.push(xxx)以達到類路由跳轉效果
    /**
     * A : 初始頁: 去中間: query1  去末 query2
     * B:  中間頁    去末:query1  + query2  回初始不帶參
     * C:  末頁    回中間:query2  回初始不帶參
     * */
    "$route.query": {
      handler(n) {
        // 如果沒有query,則展示組件1
        if (!Object.values(n).length) {
          this.currentNode = 1;
          return;
        }
        // 如果有組件2必須的query1參數且query參數爲1,則展示組件2
        if (Object.values(n).length === 1 && `query1` in n) {
          this.currentNode = 2;
          return;
        }
        // 僅判斷組件3必須參數query2,有則展示組件3
        if (`query2` in n) {
          this.currentNode = 3;
          return;
        }
        // 除此以外,均展示組件1
        this.currentNode = 1;
      },
      deep: true,
      immediate: true,
    },
  },

在從中間頁前往末頁時可能並不需要query1(中間頁本身的參數),但是我們依然要加上去,這樣傳參的目的是爲了區分末頁的上一頁是誰以實現back功能(見圖2)

而當我們在page頁中寫好了以上邏輯以後,就可以在子組件內像跳轉正常路由一樣跳轉了,不同的是跳轉的是同一個路由頁,只不過參數不同,這也是我們在寫這一類場景時要注意的點之一

假設page頁的路由名是/wzdxcsk(我真的想喫燒烤),則在組件1中想跳轉組件2/組件3則可以:

//p1.vue
// 組件1動態跳轉組件2/組件3
methods:{
  navToComp2(id,type){
    // 動態跳轉
    const queryObj = {
      2: {
        query1: id,
      },
      3: {
        query2: id,
      },
    };
    this.$router.push({
      path: "/wzdxcsk",
      query: queryObj[type],
    });
  },
}

假設組件3想要回退至上一頁則可以:

//p3.vue
// 組件3動態跳轉組件2/組件1
methods:{
 // 回退
  back() {
    this.$router.push({
      path: "/wzdxcsk",
  // 回退校驗
      query:
        `query1` in this.$route.query
          ? {
              query1: this.$route.query.query1,
            }
          : {},
    });
  },
}

整體邏輯:

在單頁面中通過變量currentNode來控制子組件顯隱,通過watch路由參數來控制具體哪個子組件顯示,通過重寫routerInstance實例的push方法來規避重複推入相同路由告警,子組件通過常規push方法實現回退&前進,前進回退時通過唯一query來判定跳轉的子組件頁面,由於url上有路由參數,則頁面在刷新後依然可以根據watch自動展示不同的組件 。

希望讀者在遇到此類需求時可以有一個參考

以上。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章