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日

相关文章

  • 9种改善AngularJS性能的方法

    下面我将详细介绍“9种改善AngularJS性能的方法”的攻略。 1. 使用ng-bind代替{{}}双花括号 在AngularJS模板中,我们使用{{}}双花括号语法绑定数据。但是,如果将其频繁使用,会导致页面性能下降。原因是{{}}会触发浏览器的$digest循环,即使只有少量数据更新。 因此,在这种情况下,可以考虑使用ng-bind指令代替{{}}。n…

    JavaScript 2023年6月11日
    00
  • JavaScript架构搭建前端监控如何采集异常数据

    JavaScript架构搭建前端监控可以通过以下几个步骤来采集异常数据: 步骤一:选择前端监控工具 在选择前端监控工具时需要考虑以下几个因素: 功能是否齐全:包括错误类型、堆栈信息、用户信息等 代码入侵程度:在引入工具时对代码的影响程度 成本:工具本身的开销以及使用后对系统性能的影响 一般来说,前端监控工具都需要通过JavaScript的方式嵌入到网站中。目…

    JavaScript 2023年5月18日
    00
  • 一个非常全面的javascript URL解析函数和分段URL解析方法

    一个非常全面的 Javascript URL 解析函数和分段 URL 解析方法 JavaScript经常被用于处理URL。这个URL解析函数能够完整、彻底地解析一个 URL 字符串,使得开发人员可以轻松地获取任何 URL。 URL 解析函数 使用以下函数来解析一个 URL: function parseURL(url) { var parser = docu…

    JavaScript 2023年6月11日
    00
  • 让你5分钟掌握9个JavaScript小技巧

    下面我就来详细讲解“让你5分钟掌握9个JavaScript小技巧”的完整攻略。 1. 变量值交换 有一种交换变量值的另类写法,可以用解构赋值完成: let a = 1; let b = 2; [a, b] = [b, a]; console.log(a) //输出2 console.log(b) //输出1 2. 使用扩展运算符复制数组 扩展运算符(spre…

    JavaScript 2023年5月17日
    00
  • 详解JavaScript的内置对象

    详解 JavaScript 的内置对象 JavaScript 是一门具有面向对象特性的编程语言,在其对面向对象编程的支持中,内置了许多常用的对象。这些对象可以帮助我们完成各种功能,包括日期计算、字符串处理、正则表达式等等。下面我们将详细讲解 JavaScript 的内置对象,以及其应用场景。 原始值包装对象 在 JavaScript 中,原始值是指字符串、数…

    JavaScript 2023年5月27日
    00
  • setinterval()与clearInterval()JS函数的调用方法

    下面是关于setInterval()和clearInterval()函数的完整攻略。 setInterval()函数 setInterval()是一个用来循环执行代码的JavaScript函数。我们可以使用它执行一些周期性任务,例如更新UI,展示动画等。 它的语法如下: var intervalID = setInterval(callback, delay…

    JavaScript 2023年6月11日
    00
  • 一文总结JavaScript中Promise遇到的问题

    一文总结JavaScript中Promise遇到的问题 Promise是什么? Promise是一种规范,主要解决了JavaScript中回调地狱的问题,可以让我们更加方便地进行异步编程。Promise主要有以下三种状态: Pending(进行中) Fulfilled(已完成) Rejected(已拒绝) Promise的基本用法 function fetc…

    JavaScript 2023年5月28日
    00
  • loading动画特效小结

    这里是“loading动画特效小结”的完整攻略: loading动画特效小结 1. 为什么需要loading动画 在网页加载的过程中,用户等待时间过长往往会让用户们感到烦躁,而且这个等待时间也是会让用户选择放弃等待,选择离开的!而为了避免这个情况产生,我们需要添加页面加载动画,以方便用户等待。 2. 实现loading动画的方法 实现loading动画有多种…

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