vuejs中如何实现三级路由并刷新页面时保持当前路由激活状态

虽互不曾谋面,但希望能和您成为笔尖下的朋友

以读书,技术,生活为主,偶尔撒点鸡汤

不作,不敷衍,意在真诚吐露,用心分享

点击左上方,可关注本刊

标星公众号(ID:itclanCoder)

如果不知道如何操作

点击这里,标星不迷路

前言

之前有介绍过vuejs中的路由的传参及路由的props配置,对于如何配置一级路由,二级路由,对于很多同学来说,基本上没什么难度

然后在配置三级路由以上的时候,往往却没那么容易,会出现一些问题

比如:如下的,二级路由下的tab选项页,当刷新页面时,还能保持当前tab的一个状态

在切换到其他路由后,在回到当前路由,依旧能保证上一个状态等

ba84fc10b2b893742931c93763afd33a.gif


01

配置一级二级路由

这里我使用的是基础模板:vue-admin-template,搭建的一个简易后台管理系统,在views/fontend/index.vue创建一个index.vue组件

并使用elementUI组件中的按钮样式的单选组合,实现一个tab按钮选项按钮的切换

以下是fontend/index.vue示例代码

<template>
    <div class="wrap">
        <el-radio-group v-model="activeTab" @change="handleRadio">
            <el-radio-button v-for="item in lists" :key="item.name"  :label="item.name">{{item.name}}</el-radio-button>
        </el-radio-group>     
        <router-view></router-view>
    </div>
</template>
<script>
    export default {
        name: 'FontEnd',
        data(){
            return {
              activeTab: '',
              lists: [
                {
                    // path: '/fontend/html',  
                    name: 'html'
                },
                {
                    // path: "/fontend/javascript",
                    name: 'javascript'
                },
                {
                    // path: "/fontend/css",
                    name: "css"
                }
              ]
           };
        },
        methods: {
            handleRadio(val) {
                this.$router.push({
                    name: val
                })
            }
        },
        watch: {
            $route: {
                handler(val) {
                    console.log(val);
                    this.activeTab = val.name;
                },
                immediate:true
            }
        }
    }
</script>
<style lang="scss" scoped>
.wrap {
    padding: 30px;
}
</style>

在上面的模板代码中,使用了elementUI中的单选按钮组合组件el-radio-group,并结合v-for指令循环遍历了一个数组

同时在点击按钮时,绑定了一个change事件,使用编程式导航this.$router.push({})控制按钮路由的跳转


02

router.js路由配置

如下是router.js路由配置

import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
/* Layout */
import Layout from '@/layout'
export const constantRoutes = [
  
  {
    path: '/fontend',
    component: Layout,
    name:'Font',
    children: [
      {
        path: '',
        component: () => import('@/views/fontend/index'),
        hidden: true,
        redirect: '/fontend/html',
        // meta: { title: '前端', icon: 'nested' },
        children: [
          {
            path: 'html',
            name: 'html',
            component: () => import("@/views/fontend/html.vue"),
            meta: {
              title: 'html'
            }
          },
          {
            path: 'javascript',
            name: 'javascript',
            component: () => import("@/views/fontend/javascript.vue"),
            meta: {
              title: 'javascript'
            }
          },
          {
            path: 'css',
            name: 'css',
            component: () => import("@/views/fontend/css.vue"),
            meta: {
              title: 'css'
            }
          },
        ]
      }
    ],
    meta: {
      title: '前端',
      icon: 'nested'
    }
  },
]
const createRouter = () => new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})
const router = createRouter()
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}
export default router

router.js中,使用children配置了子路由,并使用异步路由懒加载形式,配置了三个子路由


03

路由不显示

页面右侧不显示,子路由未配置


04

按钮的激活状态不显示

未设置初始值

<script>
    export default {
        name: 'FontEnd',
        data(){
            return {
              activeTab: '', // 激活状态
              lists: [
                {
                    // path: '/fontend/html', 
                    name: 'html'
                },
                {
                    // path: "/fontend/javascript",
                    name: 'javascript'
                },
                {
                    // path: "/fontend/css",
                    name: "css"
                }
              ]
           };
        },
        methods: {
            handleRadio(val) {
                this.$router.push({
                    name: val
                })
            }
        },
        watch: {
            $route: {
                handler(val) {
                    console.log(val);
                    this.activeTab = val.name;
                },
                immediate:true
            }
        }
    }
</script>

05

刷新页面,按钮不显示当前状态

未监听路由,并且设置激活状态

watch: {
    $route: {
        handler(val) {
            this.activeTab = val.name
        },
        immediate: true   // 一加载页面时,就自动触发上面的handler函数
    }
}


06

解决切换闪烁抖动问题

当切换按钮的时候,出现页面抖动

layout/components/AppMain.vue文件中,去掉transitionname='fade-transform'即可

<transition  mode="out-in">
    <router-view :key="key" />
</transition>


07

配置三级路由

会配置一级,二级路由,那么配置三级,四级路由..也是一样的,路由是可以进行嵌套的,使用children属性,可以无限的嵌套下去

以下是fontend/html.vue示例代码

<template>
    <div>
        <el-tabs v-model="activeName" @tab-click="handleClick">
            <el-tab-pane
                v-for="item in tabLists"
                :key="item.name"
                :label="item.name"
                :name="item.name"
            >
            </el-tab-pane>
        </el-tabs>
        <keep-alive>
            <router-view />
        </keep-alive>
    </div>
</template>
<script>
    export default {
        name: 'Html',
        data() {
            return {
                activeName:'ATag',
                tabLists: [
                    {
                        name:'ATag'
                    },
                    {
                        name:'BTag'
                    },
                    {
                        name:'SpanTag'
                    },
               ],
            }
        },
        methods:{
            handleClick(val) {
                this.$router.push({
                    name:val.name,
                })
            }
        },
        watch: {
            $route: {
                handler(val) {
                    this.activeName = val.name
                },
                immediate: true
            }
        }
    }
</script>

上面是模板代码,使用了elementUIel-tabs组件,并使用v-for循环遍历一数组对象,同样绑定tab-click事件

使用编程式导航this.$route.push({})``,实现路由的跳转,并监听当前路由,控制当前tab`的激活状态,保持刷新页面时,仍然保持上一次的一个状态

router.js路由代码

import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
/* Layout */
import Layout from '@/layout'
export const constantRoutes = [
  {
    path: '/fontend',
    component: Layout,
    name:'Font',
    children: [
      {
        path: '',
        component: () => import('@/views/fontend/index'),
        hidden: true,
        redirect: '/fontend/a',
        children: [
          {
            path: 'html',
            name: 'html',
            component: () => import("@/views/fontend/html.vue"),
            meta: {
              title: 'html'
            },
            children: [   // 三级子路由
              {
                path: '/fontend/a',   // 这个是必须要写的
                name: 'ATag',         // 要与html.vue中的配置的name保持一一对应
                component: () => import("@/views/fontend/a.vue"),
              },
              {
                path: '/fontend/b',
                name: 'BTag',
                component: () => import("@/views/fontend/b.vue"),
              },
              {
                path: '/fontend/span',
                name: 'SpanTag',
                component: () => import("@/views/fontend/span.vue"),
              },
            ]
          },
          {
            path: 'javascript',
            name: 'javascript',
            component: () => import("@/views/fontend/javascript.vue"),
            meta: {
              title: 'javascript'
            }
          },
          {
            path: 'css',
            name: 'css',
            component: () => import("@/views/fontend/css.vue"),
            meta: {
              title: 'css'
            }
          },
        ]
      }
    ],
    meta: {
      title: '前端',
      icon: 'nested'
    }
  },
  // 404 page must be placed at the end !!!
  { path: '*', redirect: '/404', hidden: true }
]
const createRouter = () => new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})
const router = createRouter()
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}
export default router

因为a.vue,b.vue.span.vue,所属在html.vue下,所以,在router.js中,使用children配置三个子路由

  1. 注意name要与模板中的循环遍历数组对象中的name值保持一致

  2. 路由的path路径的值,是必须要写的


08

解决路由点击两下才会切换到选中激活状态

解决这个问题,监听当前下的路由就可以了的

watch: {
    $route: {
      handler(val) {
          this.activeName = val.name
      },
      immediate: true  // 自动触发上面的hander函数
    }
}

上面是通过路由的方式去实现的,但是会出现一个问题,就是二级路由tab切换的状态无法保持,怎么样控制在切换三级tab选项时,刷新页面,依旧保持二级导航切换的对应激活的状态

具体解决的办法,也可以是在路由器里面,对父级路由添加标识,这种方式非常复杂,而且代码不好维护,不建议使用这种方式


09

使用动态组件方式去渲染组件

对于一级,二级可以使用路由组件去实现,但是当出现多级的时候,路由嵌套的层级太深,只会让项目代码越来越复杂,而变得不易维护

在二级导航切换按钮,显示具体的对应的内容,就可以使用动态组件的方式去渲染

html.vue使用动态组件实现

<template>
    <div>
        <el-tabs v-model="activeName" @tab-click="handleClick">
            <el-tab-pane
                v-for="item in tabLists"
                :key="item.name"
                :label="item"
                :name="item"
            >
            <!-- 使用动态组件渲染 -->
            <component :is="activeName"></component>
            </el-tab-pane>
        </el-tabs>
    </div>
</template>
<script>
    import ATag from "./a.vue";       // 引入组件
    import BTag from "./b.vue";
    import SpanTag from "./span.vue"
    export default {
        name: 'Html',
        components: {   // 注册组件
            ATag,
            BTag,
            SpanTag
        },
        data() {
            return {
                activeName:'ATag',
                tabLists: ["ATag","BTag","SpanTag"],
            }
        },
        mounted() {
            this.updateActiveTab()
        },
        methods:{
            updateActiveTab() {
                const { query } = this.$route;
                this.activeName = query.activeName || 'ATag'
            },
            handleClick() {
                const { activeName } = this;
                this.$router.push({
                    name:'html',
                    query: {
                        activeName
                    }
                })
            }
        },
    }
</script>

上面el-tabs组件的切换,具体显示哪个对应的组件,使用了动态组件component,并通过is属性,绑定activeName,以达到控制指定组件的显示

在逻辑代码中,点击tab时,使用query的方式,经过这样的操作后,即使刷新页面,也不会丢失,保持在当前状态下的

注意事项

使用query的方式,即使是刷新页面,也会保持当前的状态,但是若使用params的方式,刷新页面时,会丢失参数,并不会保持当前的状态,如果想要保持

那么就要结合localstorage,如下示例所示

上面是使用,动态组件的方式去渲染的

10

使用本地localStorage存储保持状态

如果不使用query方式,使用params的方式也是可以的,但若想保持状态,那么就需要结合localStorage进行使用,如下代码所示

代码上基本没有什么变化,代码上基本没有什么变化,只不过是使用了本地存储localStorage

<template>
    <div>
        <el-tabs v-model="activeName" @tab-click="handleClick">
            <el-tab-pane
                v-for="item in tabLists"
                :key="item.name"
                :label="item"
                :name="item"
            >
            <component :is="activeName"></component>
            </el-tab-pane>
        </el-tabs>
    </div>
</template>
<script>
    import ATag from "./a.vue";
    import BTag from "./b.vue";
    import SpanTag from "./span.vue"
    export default {
        name: 'Html',
        components: {
            ATag,
            BTag,
            SpanTag
        },
        data() {
            return {
                activeName:'ATag',
                tabLists: ["ATag","BTag","SpanTag"],
            }
        },
        mounted() {
            this.updateActiveTab()
            console.log(localStorage.getItem('activeName'));
        },
        methods:{
            updateActiveTab() {
                const { params } = this.$route;
                this.activeName = params.activeName || localStorage.getItem('activeName');
            },
            handleClick() {
                const { activeName } = this;
                localStorage.setItem('activeName',activeName);  // 设置本地存储
                this.$router.push({
                    name:'html',   // 如果使用params方式,就需要使用name方式,如果是query,也可以写具体的path:'/font/html'
                    params: {
                        activeName
                    }
                })
            }
        },
    }
</script>

针对这种三级或三级以上的,使用路由的方式,不是不可以,但是当路由嵌套的层次太多,就会使组件变得复杂起来

而使用动态渲染组件的方式,就极大的简化了逻辑,只需要考虑渲染显示对应的组件就可以

总结

其实,二级导航的切换,同样也是可以设计成动态组件的,具体看自己习惯用哪个,能够实现具体的业务就行,但有些情况下,设计成动态组件

明显要简单,容易得多,能够减少很多不必要的逻辑

而使用query的方式是可以保持状态的,参数会明文的出现在地止栏上,即使刷新页面,也不会丢失,而若使用params方式,想要状态不丢失,那么就要结合localStorage进行配合使用

b225e740e9307eb5f2502bc8bda1411b.png

c869c5b14533b7adadcfc8198b6f8123.png

Js中如何不借助第三个中间变量实现交换两个变量的值

2022-09-10

e236fe0529367a69bf2196567ec2c639.jpeg

JS特效2-如何实现页面的前进与后退

2022-09-09

99d00ac3091adf4f0ecbe1c8025fb1d9.jpeg

JS特效1-实现自动刷新页面

2022-09-08

7ddb57a192a381271156f642f8c90623.jpeg

关于script标签中type的使用

2022-09-07

a8f9fe8e31530a11c4d84a63ecc2392f.jpeg

如何利用微信视频号挣钱

2022-09-05

895bd7a5957e41902aa3e3c7f434c6d7.jpeg

4ef781b6dc7b5544576ae28eb8ccb76f.png

点个在看你最好看

点击左下角即可阅读原文

eb2c5f56c95eb39c5f8aa2fbb7d22a32.gif

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值