VUE动态路由

常规的路由前端控制,路由地址是静态的,后端通过匹配前端路由来派发权限;
而有时根据需求路由需要后端控制,前端不设置默认的静态路由,后端根据权限下发整个路由表,前端根据取到的内容生成路由。
静态路由存在于一个数组中,以下为一个典型的静态路由:

routes: [
	{
		path: '/',
		redirect: '/menu1'
	},
	{
		path: '/menu1',
		name: 'menu1',
		label: '菜单1',
		component: menu1
	},
	{
		path: '/menu2',
		name: 'menu2',
		label: '菜单2',
		redirect: '/menu2/submenu1',
		component: menu2,
		children: [
			{
				path: '/menu2/submenu1',
				label: '子菜单1',
				component: submenu1,
				name: 'submenu1'
			},
			{
				path: '/menu2/submenu2',
				label: '子菜单2',
				component: submenu2,
				name: 'submenu2'
			}
		]
	},
	{
		path: '/menu3',
		name: 'menu3',
		label: '菜单3',
		component: menu3
	}
]

以下是静态路由与动态路由的区别:

静态路由动态路由
path层级及地址固定path动态生成
前端存在全部路由,对于用户没有权限的路由需要在全局钩子beforeEach中过滤掉,以防止用户直接通过url访问前端仅存在有权限的路由,无需特别处理
菜单结构固定菜单结构根据后端返回可变

动态路由整体思路如下:

  1. 在beforeEach全局钩子时判断是否拉取后端路由,拉取放到store
  2. 前端需要存储一个基本本地路由内容,用来匹配后端路由绑定组件,待拉后端路由后合并
  3. 根路由的redirect、以及各个组件的redirect,这个是动态的,根据实际情况生成
  4. 最后使用addRoutes方法将路由信息加到前端路由中

以下为我创建的一个动态路由项目:
https://github.com/tanglingjia/dynamicroute

后端路由内容

后端路由只包括path、label和name这三项,node express后端返回的路由表内容如下,其中path为后台的路径配置,label是菜单显示名称,name是匹配前端路由的名称

{
	"code": 0,
	"data": [
	{
		"path": "/menu1",
		"name": "menu1",
		"label": "菜单1"
	},
	{
		"path": "/menu2",
		"name": "menu2",
		"label": "菜单2",
		"children": [
			{
			"path": "/menu2/submenu1",
			"label": "子菜单1",
			"name": "submenu1"
			},
			{
			"path": "/menu2/submenu2",
			"label": "子菜单2",
			"name": "submenu2"
			}
		]
	},
		{
			"path": "/menu3",
			"name": "menu3",
			"label": "菜单3"
		}
	]
}
拼装前端内容

前端内容除了component决定了路由绑定的实际组件以外,还可以有其他只需前端维护的内容,如本例中我将每一个一级菜单对应的图标放入了pic字段中,以便在生成菜单时使用

staticRoute: [
  {
	'name': 'menu1',
	'pic': 'el-icon-menu',
	'component': () => import('@/components/menu1/index')
  },
  {
	'name': 'menu2',
	'pic': 'el-icon-location',
	'component': () => import('@/components/menu2/index')
  },
  {
	'name': 'submenu1',
	'component': () => import('@/components/menu2/submenu1/index')
  },
  {
	'name': 'submenu2',
	'component': () => import('@/components/menu2/submenu2/index')
  },
  {
	'name': 'menu3',
	'pic': 'el-icon-document',
	'component': () => import('@/components/menu3/index')
  }
]

根据name匹配遍历staticRoute的内容,并入路由中

// 从后端取到的路由,合并本地内容
function mergeRoute (routes, staticRoutes, indexPre) {
  var index = 1
  routes.forEach((item) => {
    let currentRoute = staticRoutes.find((staticRoute) => { return staticRoute.name === item.name })
    Object.assign(item, currentRoute) // 合并对象
    var menuIndex = indexPre === '' ? index : indexPre + '-' + index
    Object.assign(item, {'index': menuIndex}) // 菜单索引
    index++
    if (item.children) {
      mergeRoute(item.children, staticRoutes, menuIndex)
    }
  })
}
路由重定向

除了后端路由,还有一个根路由’/’,用于作为整个路由访问的起点;另外有一个无权限路由’/nopermission’,当发现后端路由为空时跳转至无权限页面

rootRoute: {
  'path': '/'
},
noPermissionRoute: {
  'path': '/nopermission',
  'name': 'nopermission',
  'component': () => import('@/components/nopermission/index')
}

拼装顺序为:根路由,后端路由,无权限路由
制作路由重定向的规则:根路由的redirect为它后面的路由,这样如果有后端路由说明有权限,跳转到第一个后端路由,如果没有后端路由,跳转至nopermission;后端路由,递归寻找它下面是否有children,有的话redirect到children第一项

// 制作redirect,包括根路由和各级子路由
function makeRedirect (routes) {
  routes[0].redirect = routes[1].path // 根路由跳转至第一个路由path
  for (let i = 1; i < routes.length - 1; i++) {
    subRedirect(routes[i])
  }
}
// 递归生成redirect
function subRedirect (route) {
  if (route.children) {
    route.redirect = route.children[0].path // 路由redirect为其children第一项的path
    route.children.forEach((item) => {
      subRedirect(item) // 递归所有子路由
    })
  }
}

这样一来,有关制作动态路由全部结束,我将其都放在了store的mutation方法中

const mutations = {
  [types.PULL_ROUTES] (state, data) {
    mergeRoute(data, state.staticRoute) // 合并本地静态内容到后端路由
    state.routes.push(state.rootRoute) // 加入根路由
    state.routes = state.routes.concat(data) // 加入后端路由
    state.routes.push(state.noPermissionRoute) // 加入无权限路由
    makeRedirect(state.routes) // 路由重定向
  }
}

beforeEach中派发,再调用addRoute添加到前端路由

await store.dispatch(types.PULL_ROUTES, data)
router.addRoutes(store.state.common.routes)
根据路由渲染菜单

根据store中的路由信息按逻辑渲染菜单,点击相应菜单时调用路由跳转方法this.$router.push

首次访问时路由生效

目前已经解决动态路由的生成以及点击菜单路由,但是有一个问题:当首次访问页面时路由并不生效,刷新页面时也是一样,这是因为调用addRoutes后的路由并不能即时生效,导致没有相关路由
解决办法是在addRoutes后,将next()替换为next({ …to }),这样,路由守卫会判断此时跳转到新的路由,beforeEach再次触发,而在新的钩子中store中已有路由表,直接next()

当前菜单高亮

elementui菜单的高亮为default-active这个参数,再刷新页面时,需要根据当前路由找到需要高亮的菜单,添加逻辑到computed方法,返回菜单index。this.$route.matched返回的是当前匹配的路由的数组,如下为我的高亮方法,因为使用的elementui两级菜单,所以较复杂一点

currentActive () {
  let currentPath
  if (this.$route.matched.length > 0) {
	currentPath = this.$route.matched[0].path
	for (var i = 1; i < store.state.common.routes.length - 1; i++) {
	  if (store.state.common.routes[i].path === currentPath) {
		if (this.$route.matched.length === 1) {
		  return store.state.common.routes[i].index.toString()
		} else if (this.$route.matched.length === 2) {
		  currentPath = this.$route.matched[1].path
		  for (var j = 0; j < store.state.common.routes[i].children.length; j++) {
			if (store.state.common.routes[i].children[j].path === currentPath) {
			  return store.state.common.routes[i].children[j].index.toString()
			}
		  }
		}
	  }
	}
  }
}

动态路由页面效果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值