Vue动态菜单、动态路由加载以及刷新踩坑实战
在Vue项目中,动态菜单与动态路由的实现可以大大提升项目的扩展性和可维护性。本文将详细讲解如何实现Vue项目中的动态菜单、动态路由加载以及刷新踩坑实战。
一、动态菜单的实现
数据结构设计
首先,我们需要设计动态菜单的数据结构。我们可以将菜单数据放在以数组为基础的对象数据中,其中包含每个菜单的title
、icon
、path
等信息。可以如下所示:
[{
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>
其中我们引用了基于元素的递归组件MenuItem
,MenuItem
组件能够根据菜单数据动态显示菜单项,并支持子菜单下拉显示。
菜单数据的获取
我们可以将菜单数据放在单独的菜单配置文件中,通过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技术站