# 路由和菜单
在el-admin-layout中,路由和菜单是分离的,也就是说,你在定义完路由后,还需要定义一份菜单并传递给el-admin-layout:
import {appMutations} from 'el-admin-layout'
//定义路由
const routes = [{
path: '/',
component: Layout,
children: [
{
path: 'index',
component: Index,
meta: {title: '首页'}
}
]
}]
const menus = [{
fullPath: '/',
meta: {title: '根菜单'},
children: [{
fullPath: '/index',
meta: {title: '首页'}
}]
}]
//传递给el-admin-layout
//这一步可以在放在适当的时机执行,比如像服务器请求数据成功后
appMutations.menus(menus)
可以看到路由和菜单的配置是比较相似的,这样能够方便地将路由转换成菜单
注意
el-admin-layout接收到的菜单会全部显示,如果想要某个菜单不显示(常见的权限控制),请传递过滤后的菜单
强烈要求
如果在传递了菜单之后,想对菜单数组做增减,请调用appMutations.menus
(在菜单数据量较大时,可能会有性能问题)
如果想修改菜单的meta,可以通过调用appMutations.modifyMenuMeta来实现
尽量不要通过appGetters.menus
来修改,尽管这样可行,官方文档 (opens new window)
# 路由标识
每个路由都需要有一个独一无二的标识,这个标识会用在缓存、刷新、跳转、避免路由组件复用等方面
路由标识的获取:
如果路由meta中的
usePathKey
为true
,则使用route.path
作为路由唯一标识如果路由meta中的
useFullPathKey
为true
,则使用route.fullPath
作为路由唯一标识如果路由中的
name
有值,则使用route.name
作为路由唯一标识,否则使用route.fullPath
源码
function getRouterKey(route) {
const {name, path, fullPath, meta: {usePathKey, useFullPathKey} = {}} = route
return usePathKey
? path
: useFullPathKey
? fullPath
: name || fullPath
}
# 页签
页签栏上的页签数组存放于tagsViewStore.visitedViews
中
基本上,页签对象的数据结构可以认为是meta.title
恒不为空的路由对象(vue-router.Route)
想要在页签栏上显示,那么路由meta必须要有标题(title
非空或dynamicTitle
返回值非空)
页签分为固定显示的页签和路由生成的页签两种:
# 固定显示的页签
在页签栏初始化后,遍历传入el-admin-layout的菜单数组,菜单meta的affix
为true
时, 调用vue-router.resolved
获取菜单对应的路由对象,若路由对象拥有非空title,则加入页签数组。
固定显示的页签没有关闭按钮,但可以通过tagsViewMutations
来强行控制
注意
如果菜单需要固定显示,那么对应的路由对象不要使用meta.dynamicTitle
这类需要传入当前路由的函数型属性,
因为生成固定页签的时机是在页签栏初始化后,那么传入的路由可能不是期望的当前路由
# 路由生成的页签
当路由发生变化时,如果目标路由meta中的dynamicTitle
为函数类型且返回了非空的值或者title
非空,那么将目标路由加入页签数组
# 和缓存的关系
是否在页签栏上显示和是否能够缓存没有必然关系!
一个路由虽然不会显示在页签中,但它仍有机会被缓存(虽然基本没有应用场景)
# 缓存
el-admin-layout默认开启了缓存功能,如果发现缓存不了,请先确认是否启用了页签栏并且开启了页签栏的缓存功能:
//两者必须同时为true
tagsViewGetters.enabled === true && tagsViewGetters.enableCache === true
此外,el-admin-layout不支持深度大于2的嵌套路由的缓存,如果有这种路由,请将其转换为深度为2的路由!
转换前:
const routes = {
path: '/',
component: Layout,
children: [
{
path: 'nested',
component: Nested,
children: [
{
path: 'child',
component: Child
}
]
}
]
}
转换后:
//如果Nested组件使用了公用的组件,那么Child可以用动态组件<component/>重构
const routes = {
path: '/',
component: Layout,
children: [
{
path: 'nested/child',
component: Child
}
]
}
# 缓存规则
在开启了缓存功能的前提下,一个路由页面想要被缓存需要满足一些条件:
如果路由meta中的
noCache
不为true
,那么继续下面的判断,否则不缓存如果路由的唯一标识不是
name
,缓存路由的唯一标识是
name
,并且和页面组件的name
相同时才缓存,否则不缓存:
const routes = [{
path: '/',
component: Layout,
children: [
{
path: 'index',
name: 'index', // 这里的name需要和IndexPage的name相同
component: IndexPage,
}
]
}]
更详细的信息可以查看tagsViewMutations.addCacheOnly
方法和<KeepViewAlive/>
组件源码
# 内嵌iframe
只要路由meta中的iframe
有值,就视为iframe,跳转到该路由时将打开一个src为iframe
的iframe
如果两个不同路由使用了相同的iframe
值,那么他们会打开同一个iframe,除此之外iframe的缓存条件和其他页面没有区别
小优化
如果路由meta设置了iframe
,那么component
可以不用设置,避免不必要的页面渲染
# 外链
当菜单中的fullPath
以'http'
开头时,则视为外链,其被点击时将使用window.open(fullPath)
来打开一个新窗口
注意
如果外链菜单是根节点并且有子级菜单:
const externalMenu = {
fullPath: 'https://www.baidu.com',
children: [...]
}
那么在导航模式为'mix'
时,点击该菜单会直接打开新页面,而不会切换子级菜单
所以最好将外链设为叶子菜单
# 高亮菜单
导航菜单(侧边栏或顶部菜单)是根据菜单的fullPath
是否为route.path
、route.meta.activeMenu
其一来控制当前高亮的菜单的
比如有如下菜单:
const menu = {fullPath: '/test'}
当路由为'/test'
或'/test?x=1'
时,这个菜单都会被高亮
一些情况下,路由没有对应的菜单(比如详情页),需要通过菜单meta的activeMenu
自行指定一个高亮的叶子菜单:
const route = {
path: '/dynamic/:id',
component: Dynamic,
meta: {
activeMenu: '/list',
dynamicTitle: route => `动态页${route.params.id}`
}
}
const menu = {
fullPath: '/list',
meta: {
title: '列表'
}
}
WARNING
除非是外链,否则不要在fullPath
中使用url参数,比如:
const menu = {fullPath: '/test?x=1'}
不管路由是'/test'
还是'/test?x=1'
,这个菜单都不会被高亮
除非设置了菜单meta的activeMenu
(但是默认的面包屑会出问题):
const menu = {fullPath: '/test?x=1', activeMenu: '/test?x=1'}
# 避免组件复用
如果有不同路由使用了相同的组件,请确保这些路由的唯一标识不同,否则会出现组件复用的情况:
const routes = [
{
path: '/index',
name: 'index',
component: ReusablePage
},
{
path: '/index2',
name: 'index2',
component: ReusablePage
},
{
path: '/detail/:id',
component: ReusablePage,
meta: {
usePathKey: true
}
}
]