Live2d Test Env

单页面下模拟路由跳转

项目需求:一个路由页面下展示多个页面,且切换时要像真正的路由一样可以携带路由参数

三个前置知识:

  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自动展示不同的组件 。

希望读者在遇到此类需求时可以有一个参考

以上。

posted @ 2023-11-21 16:55  致爱丽丝  阅读(16)  评论(0编辑  收藏  举报