vue动态菜单、动态路由加载以及刷新踩坑实战

Vue动态菜单、动态路由加载以及刷新踩坑实战

在Vue项目中,动态菜单与动态路由的实现可以大大提升项目的扩展性和可维护性。本文将详细讲解如何实现Vue项目中的动态菜单、动态路由加载以及刷新踩坑实战。

一、动态菜单的实现

数据结构设计

首先,我们需要设计动态菜单的数据结构。我们可以将菜单数据放在以数组为基础的对象数据中,其中包含每个菜单的titleiconpath等信息。可以如下所示:

[{
  title: '首页',
  icon: 'el-icon-s-home',
  path: '/'
}, {
  title: '业务管理',
  icon: 'el-icon-document',
  children: [{
    title: '订单管理',
    path: '/order'
  }, {
    title: '客户管理',
    path: '/customer'
  }]
}]

菜单的渲染

接下来,我们需要在Vue组件中渲染菜单。这里我们可以使用基于元素的递归组件。如下所示:

<template>
  <el-menu v-if="menu" :default-active="defaultActivePath" class="menu" :router="true" mode="horizontal">
    <menu-item v-for="item in menu" :key="item.path" :element="item" />
  </el-menu>
</template>

<script>
  import MenuItem from '@/components/Menu/MenuItem.vue'

  export default {
    name: 'Menu',
    components: {
      MenuItem
    },
    props: {
      menu: {
        type: Array,
        default: null
      },
      defaultActivePath: {
        type: String,
        default: '/'
      }
    }
  }
</script>

其中我们引用了基于元素的递归组件MenuItemMenuItem组件能够根据菜单数据动态显示菜单项,并支持子菜单下拉显示。

菜单数据的获取

我们可以将菜单数据放在单独的菜单配置文件中,通过Vue的生命周期函数在组件的created阶段获取菜单数据并进行相应的处理。如下所示:

<template>
  <div class="app">
    <Menu :menu="menu" />
    <router-view />
  </div>
</template>

<script>
  import Menu from '@/components/Menu/Menu.vue'
  import menuConfig from '@/config/menu.js'

  // ...

  export default {
    name: 'App',
    components: {
      Menu
    },
    data() {
      return {
        menu: null
      }
    },
    created() {
      this.menu = menuConfig
    }
  }
</script>

至此,我们就完成了动态菜单的实现。

二、动态路由的实现

路由表的配置

我们需要在router.js中配置路由表,并根据菜单数据动态生成路由表中的路由项。

我们可以通过循环菜单数据并生成路由表中的路由项,同时,我们可以将路由路径与组件动态绑定,从而实现路由的动态加载。如下所示:

const options = {
  mode: 'history',
  base: '/',
  routes: []
}

const menuRoutes = generateRoutesFromMenu(menuConfig)

function generateRoutesFromMenu(menu = [], routes = []) {
  for (const item of menu) {
    if (item.path) {
      const route = {
        path: item.path,
        name: item.name || item.path,
        component: null,
        meta: item.meta || {}
      }

      try {
        const component = item.component;
        route.component = () => import(`@/views/${component}.vue`)
      } catch (error) {}

      if (item.children) {
        route.children = []
        generateRoutesFromMenu(item.children, route.children)
      }

      routes.push(route)
    } else if (item.children) {
      generateRoutesFromMenu(item.children, routes)
    }
  }
  return routes
}

options.routes = [
  {
    path: '/',
    redirect: { name: 'Home' }
  },
  ...menuRoutes,
  {
    path: '*',
    name: '404',
    component: () => import(`@/views/errors/404.vue`)
  }
]

其中,generateRoutesFromMenu函数中,我们通过import()函数动态加载组件,实现了路由的动态加载。

路由的注册

最后,我们需要在Vue实例中注册路由。我们可以在Vue实例的created生命周期中使用Vue Router的addRoutes方法实现路由的注册。如下所示:

<script>
  import router from '@/router'

  export default {
    name: 'App',
    components: {
      Menu
    },
    data() {
      return {
        menu: null
      }
    },
    created() {
      this.menu = menuConfig
      const menuRoutes = generateRoutesFromMenu(menuConfig)
      router.addRoutes(menuRoutes)
    }
  }
</script>

至此,我们就完成了动态路由的实现。

三、刷新问题的处理

在实现动态路由时,我们需要处理刷新页面时路由失效的问题。为了解决这个问题,我们需要重写Vue Router的push方法。

我们重写的push方法需要在路由尚未加载时,先加载对应的路由组件。我们可以通过Vue Router提供的getMatchedComponents方法获取路由所匹配的组件列表,从而判断这些组件是否已经加载。

代码如下所示:

import router from '@/router'

// 重写 router.push 方法
router.originalPush = router.push
router.push = function push(location, onComplete, onAbort) {
  router.originalPush.call(this, location, onComplete, onAbort).catch(err => {
    if (err && err.name === 'NavigationDuplicated') {
      console.log('重复路由')
    } else if (onComplete && !onAbort) {
      console.log('加载路由组件中...')
      router.isReady().then(() => {
        const matched = router.currentRoute.value.matched
        const needLoadComponents = matched.slice(matched.findIndex(r => r.path === location.path) + 1).filter(r => !r.instances.default)
        Promise.all(needLoadComponents.map(c => c.component()))
      }).then(() => {
        router.originalPush.call(this, location, onComplete, onAbort)
      })
    }
  })
}

在这里,我们保存了原始的push方法,并定义了一个新的push方法,这个新方法在路由尚未加载完成时,会先加载对应的路由组件,然后再执行原有的push方法。

示例说明一

对于一个有多种角色类型的系统,我们需要根据不同的角色类型显示不同的菜单,并且根据不同的角色类型控制路由访问权限。例如,管理员可以访问全部路由,而普通用户只能访问其具有权限的路由。

为了实现这个功能,我们可以在动态加载路由时,同时实现路由的访问权限验证。在每个路由的meta中,我们可以添加role字段,表示该路由所需要的角色类型。

在判断当前路由是否可以访问时,我们可以根据用户的角色类型,判断当前路由所需要的角色类型是否与用户的角色类型一致。如果一致,则该路由可以访问。

function generateRoutesFromMenu(menu = [], routes = []) {
  for (const item of menu) {
    if (item.path) {
      const route = {
        path: item.path,
        name: item.name || item.path,
        component: null,
        meta: {
          role: item.role || null
        }
      }

      try {
        const component = item.component;
        route.component = () => import(`@/views/${component}.vue`)
      } catch (error) {}

      if (item.children) {
        route.children = []
        generateRoutesFromMenu(item.children, route.children)
      }

      routes.push(route)
    } else if (item.children) {
      generateRoutesFromMenu(item.children, routes)
    }
  }
  return routes
}

router.beforeEach((to, from, next) => {
  const role = getUserRole();
  if (!role) {
    next({ name: 'Login' })
  } else if (to.meta.role !== null && to.meta.role !== role) {
    next({ name: 'Error', query: { statusCode: 403, message: '没有访问权限' } })
  } else {
    next()
  }
})

在这里,我们通过getUserRole()方法获取用户的角色类型。在beforeEach中,我们判断当前路由是否可以访问。如果可以访问,则允许路由跳转;如果不能访问,则跳转到错误页面,并给出相应提示。

示例说明二

对于一些需要异步加载的路由组件(例如:推荐模块、热搜模块等),在第一次访问时,我们需要优先展示非异步组件,以提升用户体验。

为了实现这个功能,我们可以在路由尚未加载完成时,先加载对应的路由组件,然后再执行原有的push方法。这里我们使用Vue Router提供的isReady方法来判断路由是否加载完成,如果没有加载完成,则异步加载路由组件。

router.originalPush = router.push
router.push = function push(location, onComplete, onAbort) {
  router.originalPush.call(this, location, onComplete, onAbort).catch(err => {
    if (err && err.name === 'NavigationDuplicated') {
      console.log('重复路由')
    } else if (onComplete && !onAbort) {
      console.log('加载路由组件中...')
      router.isReady().then(() => {
        const matched = router.currentRoute.value.matched
        const needLoadComponents = matched.slice(matched.findIndex(r => r.path === location.path) + 1).filter(r => !r.instances.default)
        Promise.all(needLoadComponents.map(c => c.component()))
      }).then(() => {
        router.originalPush.call(this, location, onComplete, onAbort)
      })
    }
  })
}

在这里,我们判断位于location路径后的所有路由,如果路由组件没有加载,则异步加载所有未加载的路由组件。加载完成后,再执行router.push方法跳转路由。

总结

通过本文,我们详细讲解了Vue动态菜单、动态路由加载以及刷新踩坑实战的相关知识。其中,我们通过设计菜单的数据结构、渲染菜单以及获取菜单数据,实现了动态菜单的加载;通过配置路由表、注册路由以及重写push方法,实现了动态路由的加载和访问权限控制;通过重写push方法,解决了刷新页面时路由失效的问题。

同时,我们还给出了两个实际应用示例,分别是:动态路由权限控制和异步路由组件加载。希望这些知识可以对您有所启发,提升您的Vue项目开发技能。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:vue动态菜单、动态路由加载以及刷新踩坑实战 - Python技术站

(0)
上一篇 2023年6月11日
下一篇 2023年6月11日

相关文章

  • javascript测试题练习代码

    我来为你详细讲解如何练习JavaScript测试题,并给出两条示例说明。 简介 练习JavaScript测试题,是学习JavaScript的必备环节。它可以帮助你积累知识,增强代码能力,提高解决问题的能力,还可以为你理解实际项目开发中的问题打下坚实的基础。 1. 掌握基础知识 在练习JavaScript测试题之前,你需要掌握基础知识。比如:变量、数据类型、函…

    JavaScript 2023年5月18日
    00
  • 浅谈Javascript面向对象编程

    下面是“浅谈Javascript面向对象编程”的完整攻略,包括了面向对象编程的基本概念、Javascript中面向对象编程的实现方式以及示例说明。 基本概念 面向对象编程(OOP)是一种编程范式,它将程序中的数据和操作封装在一起,通过对象之间互相交互实现程序的功能。在面向对象编程的范式中,对象是程序的基本单位,每个对象拥有自己的属性和方法。 面向对象编程通过…

    JavaScript 2023年5月27日
    00
  • js比较日期大小的方法

    需要比较日期大小的场景在JavaScript开发中非常常见,下面给出几种不同的比较日期大小的方法,供大家参考。 通过日期对象的valueOf()方法比较 JavaScript中日期对象有一个valueOf()方法,可以返回从1970年1月1日00:00:00起到该日期对象所代表的时间的毫秒数,因此可以通过比较两个日期对象的valueOf()方法返回值大小来判…

    JavaScript 2023年5月27日
    00
  • 浅谈JS的二进制家族

    浅谈JS的二进制家族 什么是二进制? 在计算机系统中,数值一般用二进制表示,即只有 0 和 1 两种状态。在 JavaScript 中,二进制数可以以 0b 或 0B 表示。 示例1:将十进制数转化为二进制数 const num = 10; const binaryNum = num.toString(2); console.log(binaryNum); …

    JavaScript 2023年5月27日
    00
  • json格式化/压缩工具 Chrome插件扩展版

    下面是关于“json格式化/压缩工具 Chrome插件扩展版”的详细攻略。 什么是json格式化/压缩工具 Chrome插件扩展版? JSON格式化/压缩工具是一款Chrome浏览器插件扩展。它可以将json格式的数据进行格式化或压缩,方便展示和阅读,在前端开发中有着广泛的应用。 安装和使用 步骤一:下载并安装插件 首先,我们需要在Chrome网上应用商店中…

    JavaScript 2023年5月27日
    00
  • 详解微信小程序开发聊天室—实时聊天,支持图片预览

    详解微信小程序开发聊天室——实时聊天,支持图片预览 背景 微信小程序是一种轻巧的应用程序,用户可以使用它们在微信中进行各种任务。微信小程序可以从主屏幕、公众号信息、小程序搜索结果、分享链接等任何场景下进入。开发微信小程序可以使用前端开发技术,比如HTML、CSS和JavaScript。 本篇攻略将详细讲解如何开发一个实时聊天室,支持图片预览的微信小程序应用程…

    JavaScript 2023年6月11日
    00
  • JavaScript使用math.js进行精确计算操作示例

    下面是“JavaScript使用math.js进行精确计算操作”的完整攻略。 第一部分:什么是math.js? math.js是一个用于数学计算的JavaScript库,它提供了大量的数学函数和工具,其中包括高级数学、矩阵计算、分数运算、单位转换和随机数生成等。 第二部分:使用math.js进行精确计算 在JavaScript中,浮点数计算可能会产生精度问题…

    JavaScript 2023年5月28日
    00
  • JS常见错误(Error)及处理方案详解

    JS常见错误(Error)及处理方案详解 JavaScript是一种弱类型语言,当我们编写JavaScript代码时,难免会出现错误。遇到这些错误时,可以通过了解常见的错误类型以及如何处理它们来提高我们的调试能力和代码质量。本文将介绍几种常见的JS错误,以及如何处理它们。 类型错误(TypeError) 当我们试图在一个不允许使用特定方法或属性的数据类型上使…

    JavaScript 2023年5月18日
    00
合作推广
合作推广
分享本页
返回顶部