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

yizhihongxing

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日

相关文章

  • js中setTimeout()与clearTimeout()用法实例浅析

    js中setTimeout()与clearTimeout()用法实例浅析 setTimeout() setTimeout()是JavaScript内置的函数,用来在指定的时间后执行一段代码。 语法: setTimeout(function, milliseconds, param1, param2, …) 参数解释:- function: 必选项,要执行…

    JavaScript 2023年6月10日
    00
  • javascript实现花样轮播效果

    JavaScript实现花样轮播效果攻略 轮播效果是Web开发中经常使用的交互元素,下面我们将介绍如何使用JavaScript实现花样轮播效果。 实现思路 实现花样轮播效果的主要思路是:动态的改变轮播项的位置,实现轮播效果。该实现思路可以通过列表型轮播和可视区域轮播两种方式实现。 列表型轮播 列表型轮播是将所有轮播项放在一个容器中,通过改变容器的宽度和定位属…

    JavaScript 2023年6月10日
    00
  • JavaScript+Canvas实现彩色图片转换成黑白图片的方法分析

    JavaScript+Canvas实现彩色图片转换成黑白图片的方法分析 在网页制作中,为了减少页面的加载时间和提高用户体验,很多时候需要将彩色图片转换为黑白图片。这里提供使用JavaScript+Canvas实现彩色图片转换为黑白图片的方法: 1. 获取图片 首先需要获取图片文件,可以通过以下方法: var img = new Image(); img.sr…

    JavaScript 2023年5月28日
    00
  • JavaScript高级程序设计 读书笔记之十一 内置对象Global

    JavaScript高级程序设计 读书笔记之十一 内置对象Global 什么是全局作用域 在JavaScript中,全局作用域是整个程序的最高层次。全局变量会成为全局作用域的属性,全局函数会成为全局作用域的方法。在全局作用域中定义的变量和函数,在整个程序范围内都是可用的。 Global对象简介 Global是JavaScript中的一个全局对象,它不是构造函…

    JavaScript 2023年5月27日
    00
  • 关于javascript event flow 的一个bug详解

    关于 “关于javascript event flow 的一个bug详解” 的攻略,我会详细介绍以下内容: 什么是 JavaScript 事件流 什么是事件捕获和事件冒泡 JavaScript 事件流的 bug 如何解决 JavaScript 事件流的 bug 首先,我们需要了解什么是 JavaScript 事件流。 JavaScript 事件流 JavaS…

    JavaScript 2023年6月11日
    00
  • JS+HTML5手机开发之滚动和惯性缓动实现方法分析

    JS+HTML5手机开发之滚动和惯性缓动实现方法分析 简介 在移动端开发中,实现滚动和惯性缓动是非常常见的功能,本文将基于JS和HTML5,详细讲解实现这一功能的方法和实现过程。本文中的代码及示例在iOS和Android均测试通过。 方法分析 滚动和惯性缓动可以通过使用CSS3的transform属性进行实现,如下所示: .container { trans…

    JavaScript 2023年6月11日
    00
  • js表单元素checked、radio被选中的几种方法(详解)

    当我们需要在Web页面中收集用户输入时,表单是不可缺少的工具之一。而表单元素中的checkbox和radio这两种类型的输入框对于选项的选择有着重要的作用。然而,如何通过JavaScript获取选中的checkbox或radio的值呢?下面我们将详细讲解这个问题。 1. checked属性 对于单个的checkbox,我们可以通过其checked属性来检查其…

    JavaScript 2023年6月10日
    00
  • JavaScript高级程序设计 阅读笔记(十九) js表格排序

    下面是针对”JavaScript高级程序设计 阅读笔记(十九) js表格排序”的完整攻略: 阅读笔记简介 这篇阅读笔记主要讲解了如何使用JavaScript进行表格排序。通过这篇笔记,我们可以学习到如何使用JavaScript实现表格排序的几种方式,包括基于DOM操作、使用jQuery库、第三方SortableJS库和自定义tableSorter库等。 方法…

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