vue-router的addRoute方法实现权限控制
在项目实践中,往往需要用户登录后由后端返回用户的权限,来动态配置路由,vue-router提供了两个方法router.addRoutes(),router.addRoute()可以进行动态路由配置,这里需要注意的是vue-router的options属性并不是响应式的,在添加(修改)路由规则后,router.options.routes属性不会改变,目前,如果需要更新router.options.
在项目实践中,往往需要用户登录后由后端返回用户的权限,来动态配置路由,vue-router提供了两个方法router.addRoutes(),router.addRoute()可以进行动态路由配置,这里需要注意的是vue-router的options属性存放的是路由配置的信息,并不是响应式的,在动态添加路由规则后,router.options.routes属性不会改变,如果需要更新router.options.routes,则需要手动更改。
下面的代码基于vue-router版本:3.5.1,版本的变化可能会存在一些不同,如使用已废弃的router.addRoutes方法会出现警告。
参考:https://router.vuejs.org/zh/api/#router-addroutes
router.addRoutes 和router.addRoute 新路由规则
-
router.addRoutes 添加多条新路由规则
router.addRoutes(routes: Array<RouteConfig>)
动态添加更多的路由规则。参数必须是一个符合 routes 选项要求的
数组
。 -
router.addRoute 添加一条新路由规则
如果该路由规则有 name,并且已经存在一个与之相同的名字,则会覆盖它。addRoute(route: RouteConfig): () => void
addRoute方法可用于为现有路由添加子路由规则
addRoute(parentName: string, route: RouteConfig): () => void
在项目实践时,通常在用户登录后获取到用户的权限表,可以将这些数据保存到前端。
下面是一个简单的例子,忽略了很多细节,旨在演示通过路由进行权限控制的方法。
-
router.beforeEach()中进行全局前置守卫
- 如果路由地址在白名单中则放行(例外:如果路由是登录并且存在token,重定向到首页)
- 不在白名单,且无token,重定向到登录页
- 不在白名单,且有token,放行
-
在登录时用setTimeout方法模拟异步获取动态路由和token,并保存到localStorage中,调用generateRoutes方法添加路由规则。
-
在页面手动刷新时,配置的动态路由会消失需要在
router.onReady()
方法中,再次调用generateRoutes方法。 -
generateRoutes方法遍历动态路由数组,将成员转为RouteConfig,并调用addRoute方法。
用vue cli搭建了这样一个组件结构
模板具体什么样式无所谓,这里只做动态路由的展示 -
router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Layout from '../views/Layout.vue'
import { generateRoutes } from '../utils'
Vue.use(VueRouter)
const constantRoutes = [
{
path: '/',
name: 'Home',
component: Layout,
redirect: '/index',
children: [
{
path: 'index',
component: Home
},
{
path: 'about',
name: 'About',
component: () => import('../views/About.vue')
}
]
},
{
path: '/login',
name: 'login',
component: () => import('../views/Login.vue')
},
{
path: '/404',
component: () => import('../views/404Error.vue')
}
]
const createRouter = () => new VueRouter({
mode: 'history',
routes: constantRoutes
})
const router = createRouter()
// 检查是否存在于免登陆白名单
function inWhiteList (toPath) {
const whiteList = ['/login', '/404']
const path = whiteList.find((value) => {
// 使用正则匹配
const reg = new RegExp('^' + value)
return reg.test(toPath)
})
return !!path
}
router.beforeEach((to, from, next) => {
console.group('%c%s', 'color:blue', `${new Date().getTime()} ${to.path} 的全局前置守卫----------`)
console.log('所有活跃的路由记录列表', router.getRoutes())
console.groupEnd()
const token = localStorage.getItem('token')
// 检查to.path是否存在于免登陆白名单
if (inWhiteList(to.path)) {
if (to.path === '/login' && token) {
// 避免重复登录
next({ path: '/' })
} else {
next()
}
return false
}
// 判断是否已经登录,未登录则重定向到登录页(通过query传参记录原来的路径)
if (!token) {
const toPath = to.fullPath !== '/login' ? to.fullPath : '/'
next({
path: '/login',
query: { redirect: toPath }
})
} else {
next()
}
})
// 在路由完成初始导航时调用,如果有异步操作放置到这里
router.onReady(() => {
console.group('%c%s', 'color:red', `${new Date().getTime()} 路由完成初始导航----------`)
console.log('添加前-所有活跃的路由记录列表', router.getRoutes())
generateRoutes()
console.log('添加后-所有活跃的路由记录列表', router.getRoutes())
console.groupEnd()
})
// 重置路由
export function resetRouter () {
const newRouter = createRouter()
router.matcher = newRouter.matcher
}
export default router
Login.vue
<template>
<div>
<button @click="signIn">登录</button>
</div>
</template>
<script>
import { generateRoutes } from '../utils'
export default {
methods: {
signIn () {
const routes = [
{
id: 1,
parentId: 0,
path: '/sys',
component: 'Layout.vue',
redirect: '/sys/user'
},
{
id: 2,
parentId: 1,
path: 'user',
component: 'User.vue'
}
]
setTimeout(() => {
localStorage.setItem('token', '123456')
localStorage.setItem('routes', JSON.stringify(routes))
console.log('登录成功')
generateRoutes()
this.$router.push(this.$route.query.redirect || '/')
}, 1000)
}
}
}
</script>
Layout.vue
<template>
<div class="layout">
<div class="header">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> |
<router-link to="/sys">Sys</router-link> |
<router-link to="/sys/user">User</router-link>
<button @click="signOut">登出</button>
</div>
<router-view/>
</div>
</template>
<script>
import { resetRouter } from '../router/index'
export default {
methods: {
signOut () {
localStorage.removeItem('token')
localStorage.removeItem('routes')
resetRouter()
console.log('登出后的路由', this.$router.getRoutes())
this.$router.push('/login')
}
}
}
</script>
generateRoutes()
export function generateRoutes () {
const _asyncRoutes = JSON.parse(localStorage.getItem('routes'))
const routes = _asyncRoutes.map(({ id, parentId, path, component, redirect }) => {
const route = { id, parentId, path, redirect }
route.component = (resolve) => require([`../views/${component}`], resolve)
return route
})
const asyncRoutes = toTree(routes)
asyncRoutes.forEach(route => {
router.addRoute(route)
})
router.addRoute({
path: '*',
redirect: '/404'
})
}
这个有个toTree()用于将数组转成tree,来实现路由嵌套
更多推荐
所有评论(0)