Vue 进阶系列教程将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!
2013年7月28日,尤雨溪第一次在 GItHub 上为 Vue.js 提交代码;2015年10月26日,Vue.js 1.0.0版本发布;2016年10月1日,Vue.js 2.0发布。
最早的 Vue.js 只做视图层,没有路由, 没有状态管理,也没有官方的构建工具,只有一个库,放到网页里就可以直接用了。
后来,Vue.js 慢慢开始加入了一些官方的辅助工具,比如路由(Router)、状态管理方案(Vuex)和构建工具(Vue-cli)等。此时,Vue.js 的定位是:The Progressive Framework。翻译成中文,就是渐进式框架。
Vue.js2.0 引入了很多特性,比如虚拟 DOM,支持 JSX 和 TypeScript,支持流式服务端渲染,提供了跨平台的能力等。Vue.js 在国内的用户有阿里巴巴、百度、腾讯、新浪、网易、滴滴出行、360、美团等等。
Vue 已是一名前端工程师必备的技能,现在就让我们开始深入学习 Vue.js 内部的核心技术原理吧!
什么是权限控制
在我们开发项目的时候,尤其是管理后台项目中,都会遇到根据用户角色来进行相关功能的展示和隐藏。比如超级管理员可以查看所有的模块,普通用户只能看一部分模块,而且还可能会有一个菜单管理模块,可以对不同用户的角色进行相关配置。根据系统中各个角色进行相关的访问权限限制,就是我们这里说的权限控制。
什么是addRoute()
我们先看一下官方是怎么定义的:
添加一条新路由规则。如果该路由规则有name,并且已经存在一个与之相同的名字,则会覆盖它。
函数签名:
addRoute(route:RouteConfig):() => void
其中RouteConfig接口定义如下:
interface RouteConfig = {
path: string,
component?: Component,
name?: string, // 命名路由
components?: { [name: string]: Component }, // 命名视图组件
redirect?: string | Location | Function,
props?: boolean | Object | Function,
alias?: string | Array,
children?: Array, // 嵌套路由
beforeEnter?: (to: Route, from: Route, next: Function) => void,
meta?: any,
// 2.6.0+
caseSensitive?: boolean, // 匹配规则是否大小写敏感?(默认值:false)
pathToRegexpOptions?: Object // 编译正则的选项
}
该函数执行后产生的结果就是,会将参数route添加我们系统的路由表中。
比如下面这种:
const constantRoutes = [
{
path: "/login",
name: "login",
component: () => import("@/views/login")
},
{
path: "/home",
component: () => import("@/views/home"),
redirect: "/home",
name: "首页",
meta: { title: "首页", icon: "el-icon-s-help" }
}
];
const createRouter = () =>
new Router({
routes: constantRoutes
});
// 此时,当前路由表只有两个路由
// 下面通过addRoute方法来添加路由
const route = createRouter() // 拿到路由对象
route.addRoute({
{
path: "/hello",
component: () => import("@/views/hello"),
redirect: "/hello",
name: "你好",
meta: { title: "你好", icon: "el-icon-s-help" }
}
})
// 现在,路由表中就有三个路由了
如何进行权限控制
一般我们进行权限控制的话,有两种方案,一种是前端也保存一套路由表,一种是前端不保存路由表,路由表信息全部由后端返回。
第一种:前端保存一套路由表
我们先看第一种的实现思路:
前端保存全部页面的路由信息,并且在每个路由信息中保存当前路由对应的权限关键字。
每次用户登录成功的时候,后台返回当前用户对应的权限关键字
前端根据后台返回的权限关键字,遍历自己前端保存的路由表
将符合用户权限的路由,通过addRoute()动态添加到路由表中
具体实现如下:
第一步,在前端保存一套路由表
// route.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const constantRoutes = [
{
path: "/login",
name: "login",
component: () => import("@/views/login")
},
{
path: "/home",
component: () => import("@/views/home"),
redirect: "/home",
name: "首页",
meta: { title: "首页", icon: "el-icon-s-help" }
}
]; // 常用路由表
export const asyncRoute = [
{
path: "/asyncRoute1",
name: "asyncRoute1",
component: () => import("@/views/asyncRoute1"),
meta: {
title: "动态路由1",
icon: "el-icon-s-help",
roles: ['admin', 'ordinaryUsers']
// 当前路由对应的权限关键字,超级管理员和普通用户
}
},
{
path: "/asyncRoute2",
component: () => import("@/views/asyncRoute2"),
name: "asyncRoute2",
meta: {
title: "动态路由2",
icon: "el-icon-s-help",
roles: ['admin'],
// 当前路由对应的权限关键字,超级管理员
}
}
] // 动态理由表
const createRouter = () =>
new Router({
routes: constantRoutes // 这里只有常用路由,并没有动态路由
});
const route = createRouter()
export default route // 导出路由对象
2. 用户登录的时候,拿到后台返回的权限关键字,这里是admin或者ordinaryUsers。然后遍历动态路由表,动态添加路由。
// 引入路由对象和前端保存的动态路由表
import router,{ asyncRoute } from '@/router'
// 过滤符合用户权限的路由表
let arr = asyncRoute.filter(item=>{
return item.meta.roles.includes('这里后台返回的权限关键字')
})
// 遍历符合权限的路由表,动态添加路由
arr.forEach(item=>{
route.addRoute(item)
})
第二种:前端不保存,全部由后台返回
实现思路:
前端只保存常用路由,比如登录页面、首页等。
每次用户登录成功的时候,后台返回一个路由数组,数组中每个对象包含的信息就是我们的路由对象
前端根据后台返回路由数组,通过addRoute()动态添加到路由表中
注意:
这里有一个地方需要注意,就是路由对象中的component字段,后台只会返回给我们一个字符串,但这里前端需要的是一个组件对象。所以前端需要将对象字段转换为前端组件,然后才能创建动态路由。
具体实现如下:
1. 第一步,前端只保存常用路由。
// route.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const constantRoutes = [
{
path: "/login",
name: "login",
component: () => import("@/views/login")
},
{
path: "/home",
component: () => import("@/views/home"),
redirect: "/home",
name: "首页",
meta: { title: "首页", icon: "el-icon-s-help" }
}
]; // 常用路由表
const createRouter = () =>
new Router({
routes: constantRoutes // 这里只有常用路由
});
const route = createRouter()
export default route // 导出路由对象
2. 每次登录的时候,遍历后台返回的路由数组,然后动态添加路由。
// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap) {
const accessedRouters = asyncRouterMap.filter(route => {
if (route.component) {
if (route.component === "Layout") {
route.component = Layout;
} else {
route.component = loadView(route.component); // 导入组件
}
}
route.meta = { title: route.title, icon: route.icon || "el-icon-s-help" };
route.name = route.label;
if (route.children && route.children.length) {
route.children = filterAsyncRouter(route.children);
} else {
route.children = [];
}
return true;
});
return accessedRouters;
}
const loadView = view => {
// 路由懒加载
return resolve => require([`@/views/${view}`], resolve);
};
//过滤路由
const menus = filterAsyncRouter('后台返回的路由数组');
//动态添加路由
router.addRoutes(menus);
注意:
大家注意到了,这里我用的是addRoutes,而不是addRoute。他们两个的区别就是,addRoutes需要传入路由数组,addRoute需要传入路由对象。不过在Vue3中,addRoutes已经废弃了,需要注意一下。
两种方式的对比
第一种前端自己保存一套路由表,里面的name、path、icon等字段都是前端自己控制的,这样在前端页面跳转时,更加的稳定,但是icon字段不能动态改变,也不能动态增删路由对象,每次对应角色的权限有变化的时候,前端也需要进行改动。菜单之间的顺序是固定的。
第二种全部由后台决定,里面的name、path、icon等字段都是后台返回的,路由之间的顺序也是后台决定的。所以前台菜单的icon、顺序、名称都是可以动态改变的,这里就可以做一个叫做菜单管理的模块,用来动态配置前端菜单。
但是这里需要注意,path字段和组件也是后台返回的,此时如果后台返回的path字段和前端页面跳转的path不一样的时候,会影响前端页面的跳转,组件不一样也会导致页面无法渲染出来。所以菜单管理中,对于path和组件字段是通过下拉框形式绑定的,用户不可以根据自己的意愿随意更改,以此来减少对前端页面的影响。
Vue 进阶系列教程将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!
叶阳辉
HFun 前端攻城狮
往期精彩:
Vue 进阶系列丨Object 的变化侦测
Vue 进阶系列丨Array 的变化侦测
Vue 进阶系列丨虚拟DOM和VNode
Vue 进阶系列丨Patch 和模板编译
Vue 进阶系列丨事件相关的实例方法
Vue 进阶系列丨生命周期相关的实例方法
Vue 进阶系列丨生命周期
Vue 进阶系列丨自定义指令
Vue 进阶系列丨最佳实践
Vue 进阶系列丨Mixin 混入