11f66ca43a89ce6be577df7d753240de.png

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<string>,
  children?: Array<RouteConfig>, // 嵌套路由
  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" }
    }
})
// 现在,路由表中就有三个路由了

如何进行权限控制

一般我们进行权限控制的话,有两种方案,一种是前端也保存一套路由表,一种是前端不保存路由表,路由表信息全部由后端返回。

  • 第一种:前端保存一套路由表

我们先看第一种的实现思路:

  1. 前端保存全部页面的路由信息,并且在每个路由信息中保存当前路由对应的权限关键字。

  2. 每次用户登录成功的时候,后台返回当前用户对应的权限关键字

  3. 前端根据后台返回的权限关键字,遍历自己前端保存的路由表

  4. 将符合用户权限的路由,通过addRoute()动态添加到路由表中

具体实现如下:

  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" }
    }
];  // 常用路由表


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)
})
  • 第二种:前端不保存,全部由后台返回

实现思路:

  1. 前端只保存常用路由,比如登录页面、首页等。

  2. 每次用户登录成功的时候,后台返回一个路由数组,数组中每个对象包含的信息就是我们的路由对象

  3. 前端根据后台返回路由数组,通过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 进阶系列教程将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!

c394265f75f2ac811511a53422a905d7.png

叶阳辉

HFun 前端攻城狮

往期精彩:

Logo

前往低代码交流专区

更多推荐