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日

相关文章

  • js数组去重的11种方法

    下面是详细讲解“js数组去重的11种方法”的完整攻略。 引言 在JavaScript开发中,常常需要我们对数组进行去重的操作。由于JavaScript本身没有提供去重的方法,开发者需要结合一些技巧和方法来完成这个任务。本文将由浅入深介绍11种JavaScript数组去重的方法,希望对大家有所帮助。 方式一:使用Set类型 Set是一种新的ES6数据类型,它存…

    JavaScript 2023年5月27日
    00
  • javascript对象的创建和访问

    当我们要处理数据的时候,常常需要使用对象。JavaScript 的对象非常灵活,并且支持动态增加属性、修改属性和删除属性。 JavaScript 对象的创建 JavaScript 对象可以通过以下几种方式进行创建: 直接量法 这是一种创建对象的常用方式,直接将属性和属性值以键值对的形式包含在花括号 { } 中: let person = { name: ‘B…

    JavaScript 2023年5月27日
    00
  • 分享一款超好用的JavaScript 打包压缩工具

    下面是分享一款超好用的JavaScript打包压缩工具的完整攻略。 一、背景介绍 在前端开发中,我们经常需要使用JavaScript打包压缩工具来减小文件大小,提高网站的性能。本篇攻略将会介绍一款超好用的JavaScript打包压缩工具,并给出两个示例说明。 二、工具介绍 本文介绍的打包压缩工具是Webpack,它是现代化的 JavaScript 应用程序的…

    JavaScript 2023年5月27日
    00
  • FCKeditor 和 SyntaxHighlighter 代码高亮插件的整合

    下面是关于“FCKeditor 和 SyntaxHighlighter 代码高亮插件的整合”的详细攻略。 概述 首先我们要理解这两个插件是什么。FCKeditor是一个基于Web的WYSIWYG文本编辑器,可以方便地进行文本排版,支持多种格式,具有图形用户界面。而SyntaxHighlighter是一个代码高亮插件,可以让我们方便地将代码高亮显示,并可以自定…

    JavaScript 2023年6月11日
    00
  • Javascript Web Worker使用过程解析

    Javascript Web Worker使用过程解析 什么是Web Worker? Web Workers是HTML5提出的一项新技术,它是一种运行在后台线程的JavaScript,它允许长时间运行的脚本或计算任务不会影响用户界面的性能或响应性。也就是说,JavaScript代码的处理可以交由浏览器的后台线程中处理,以便在不影响主线程UI的情况下,加速运算…

    JavaScript 2023年5月28日
    00
  • js获取url中”?”后面的字串方法

    获取URL中”?”后面的字串,是前端常见的一种需求,本文将介绍几种获取URL参数的方法。 方法一 function getQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i&quot…

    JavaScript 2023年6月11日
    00
  • js实现随机抽奖

    下面是js实现随机抽奖的完整攻略: 目录 背景介绍 随机抽奖原理 实现过程 准备工作 代码逻辑 示例说明 示例一:随机抽取一名幸运儿 示例二:抽奖动画效果 背景介绍 随机抽奖是常见的一个功能,例如网站活动、抽奖游戏、公益机构等都有可能需要使用到此功能。本文将详细介绍如何使用JavaScript实现随机抽奖的功能。 随机抽奖原理 随机抽奖的实现原理比较简单,例…

    JavaScript 2023年6月11日
    00
  • JS在IE和FireFox之间常用函数的区别小结

    针对“JS在IE和FireFox之间常用函数的区别小结”的问题,以下是我的回答: 目的 本文主要介绍在IE和Firefox之间常用函数的区别,帮助开发者更好地适配不同的浏览器环境,并确保网站能够在各种浏览器中正常运行。 核心知识点 事件处理函数的差异 DOM API 的差异 JavaScript 对象的差异 JavaScript 数组的差异 CSS 样式的差…

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