这段时间在研究element-admin,感觉这个库有许多值得学习的地方,我学习这个库的方法是,先看它的路由,顺着路由,摸清它的逻辑,有点像顺藤摸瓜。

这个库分的模块非常清晰,适合多人合作开发项目,但是如果一个人去用这个库开发后台,步骤显的有点繁琐,特别是调用后端接口,之前一个方法就调用到了,但是用这个库,需要先后分几步调用。

比如说调用一个登陆接口:点击登陆按钮----》调用store中的方法----》调用api中对应登陆的方法---》request.js中封装的axios方法

4步!!!!,让我看来确实是有点繁琐,这个问题到后面解决,通过自己封装的axios方法,直接调用后台接口,目前不知道会不会遇到其它问题。好了,接下来进入正题!!!

接下来先介绍一下,element-admin的登录逻辑

1、先看登录方法里写什么:

handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true;
//调用user模块红的login
console.log("点击登陆按钮")
this.$store.dispatch('user/login', this.loginForm).then(() => {
console.log("登录成功");
this.$router.push({ path: this.redirect || '/' });
this.loading = false;
}).catch(() => {
this.loading = false;
})
} else {
console.log('error submit!!');
return false;
}
})
}

通过上面红色代码,可以看出,点击过登录按钮后,调用了$store里的一个方法,名叫login

2、下面来看看$store里的这个login方法:

import { login, logout, getInfo,self} from '@/api/user'

const actions = {
// user login
login({ commit }, userInfo) {
const { username, password } = userInfo;
return new Promise((resolve, reject) => {
console.log("vuex中的请求")
login({ username: username.trim(), password: password }).then(response => {
console.log('vuex中')
console.log(response);
const { data } = response;
commit('SET_TOKEN', data.token);//存在vueX中
setToken(data.token);//存在cookie中
resolve();
}).catch(error => {
console.log(error);
reject(error);
})
})
},

咿,怎么两个login,熟悉vuex的话应该知道,第一个login是$store中的方法,第二个login方法是,api里的login方法,用来调用接口的

3、再来看看api中的login方法:

import request from '@/utils/request'

export function login(data) {
return request({
url: '/user/login',
method: 'post',
data
})
}

上面是api中的login方法,它调用了request.js,request.js是封装了axios,是用来请求后台接口的,如果这个接口请求成功了,就回到了第一步中的.then()方法中

代码是,路由跳转到首页,进入正式页面!!!

重点来了!!!

之所以是称之为权限,也就是必须满足一定的条件才能够访问到正常页面,那么如果不满足呢?如果我没有token,让不让我进正常页面呢??

肯定是不让的,那我没有token,该去哪?答案是还待在登录页,哪都去不了,那么这些处理应该在哪写呢?答案是,permission.js模块

这个js在main.js引入,其实就是个路由拦截器:来看看它的代码:

import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar 一个进度条的插件
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title' NProgress.configure({ showSpinner: false }) // NProgress Configuration 是否有转圈效果 const whiteList = ['/login'] // 没有重定向白名单 router.beforeEach(async(to, from, next) => {
// 开始进度条
NProgress.start() // 设置页面标题
document.title = getPageTitle(to.meta.title) // 确定用户是否已登录
const hasToken = getToken() if (hasToken) {
if (to.path === '/login') {
// 如果已登录,则重定向到主页
next({ path: '/' })
NProgress.done()
} else {
const hasGetUserInfo = store.getters.name;
console.log(hasGetUserInfo);
if (hasGetUserInfo) {
console.log("有用户信息");
next();
} else {
console.log("无用户信息")
try {
// 获得用户信息
await store.dispatch('user/getInfo');
//实际是请求用户信息后返回,这里是模拟数据,直接从store中取
const roles=store.getters.roles;
store.dispatch('permission/GenerateRoutes', { roles }).then(() => { // 生成可访问的路由表
router.addRoutes(store.getters.addRouters); // 动态添加可访问路由表
router.options.routes=store.getters.routers;
next({ ...to, replace: true });// hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
}) // next()
} catch (error) {
// 删除token,进入登录页面重新登录
await store.dispatch('user/resetToken');
Message.error(error || 'Has Error');
next(`/login?redirect=${to.path}`);
NProgress.done();
}
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) {
// 在免费登录白名单,直接去
next()
} else {
// 没有访问权限的其他页面被重定向到登录页面。
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}) router.afterEach(() => {
// 完成进度条
NProgress.done()
})

一看代码好多,不过不要怕,我来分析一下它的情况,不就是点if else嘛

从上面代码看,每次路由跳转,都要从cookie中取token,

那么可以分两种情况,有token和无token

有token:再看看是不是去登录页的,登录页肯定不能拦截的,如果是登录页就直接放行。如果不是登录页,就要看看本地有没有用户信息,看看cookie中有没有用户信息(不一定是token,也可能是localstorage)。如果有用户信息,放行。如果没有用户信息,就调用接口去获取登录信息,然后后面还有一点代码,涉及到了动态添加路由(这里先说到这,后面具体说动态添加权限路由的事)。获取到用户信息后放行。如果在获取用户信息的过程中报错,则回到登录页

无token:先看看用户要进入的页面是不是在白名单内,一般登录、注册、忘记密码都是在白名单内的,这些页面,在无token的情况下也是直接放行。如果不在白名单内,滚回登录页。

以上就是element-admin的登录逻辑了。不知道能否帮助到你,但是写下来,让自己的思路更清晰,也是不错的。

下面来说一下,element-admin的动态权限路由,显示侧边栏是什么逻辑

首先要了解一下,侧边栏是如何渲染出来的,看看layout/components/slibar/index.vue有这样一段代码:

<sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" />

计算属性中有这样一段代码:

  routes() {
return this.$router.options.routes
},

这个routes,是路由的元信息!!!是一个数组

看到这就应该明白,侧边栏是如何渲染出来的,

再来看看router.js里的代码:

import Vue from 'vue'
import Router from 'vue-router' Vue.use(Router) /* Layout */
import Layout from '@/layout' /**
* 注意: 子菜单只在路由子菜单时出现。长度> = 1
* 参考网址: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
*
* hidden: true 如果设置为true,项目将不会显示在侧栏中(默认为false)
* alwaysShow: true 如果设置为true,将始终显示根菜单
* 如果不设置alwaysShow, 当项目有多个子路由时,它将成为嵌套模式,否则不显示根菜单
* redirect: noRedirect 如果设置noRedirect,则不会在面包屑中重定向
* name:'router-name' the name is used by <keep-alive> (must set!!!)
* meta : {
roles: ['admin','editor'] 控制页面角色(可以设置多个角色)
title: 'title' 名称显示在侧边栏和面包屑(推荐集)
icon: 'svg-name' 图标显示在侧栏中
breadcrumb: false 如果设置为false,则该项将隐藏在breadcrumb中(默认为true)
activeMenu: '/example/list' 如果设置路径,侧栏将突出显示您设置的路径
}
*/ /**
* constantRoutes
* 没有权限要求的基本页
* 所有角色都可以访问
* 不需要动态判断权限的路由
*/
export const constantRoutes = [ {
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
}, {
path: '/404',
component: () => import('@/views/404'),
hidden: true
}, {
path: '/',
component: Layout,
redirect: '/self',
children: [{
path: 'self',
name: 'Self',
component: () => import('@/views/self/index'),
meta: { title: '首页', icon: 'dashboard' }
}]
}, {
path: '/example',
component: Layout,
redirect: '/example/table',
name: 'Example',
meta: { title: 'Example', icon: 'example' },
children: [
{
path: 'table',
name: 'Table',
component: () => import('@/views/table/index'),
meta: { title: 'Table', icon: 'table'}
},
{
path: 'tree',
name: 'Tree',
component: () => import('@/views/tree/index'),
meta: { title: 'Tree', icon: 'tree',breadcrumb: true},
hidden: false,//在侧边栏上显示 true为不显示 当父路由的字路由为1个时,不显示父路由,直接显示子路由
alwaysShow:false,//默认是false 设置为true时会忽略设置的权限 一致显示在跟路由上
}
]
}, {
path: '/form',
component: Layout,
children: [
{
path: 'index',
name: 'Form',
component: () => import('@/views/form/index'),
meta: { title: 'Form', icon: 'form' }
}
]
}, {
path: '/nested',
component: Layout,
redirect: '/nested/menu1',
name: 'Nested',
meta: {
title: 'Nested',
icon: 'nested'
},
children: [
{
path: 'menu1',
component: () => import('@/views/nested/menu1/index'), // Parent router-view
name: 'Menu1',
meta: { title: 'Menu1' },
children: [
{
path: 'menu1-1',
component: () => import('@/views/nested/menu1/menu1-1'),
name: 'Menu1-1',
meta: { title: 'Menu1-1' }
},
{
path: 'menu1-2',
component: () => import('@/views/nested/menu1/menu1-2'),
name: 'Menu1-2',
meta: { title: 'Menu1-2' },
children: [
{
path: 'menu1-2-1',
component: () => import('@/views/nested/menu1/menu1-2/menu1-2-1'),
name: 'Menu1-2-1',
meta: { title: 'Menu1-2-1' }
},
{
path: 'menu1-2-2',
component: () => import('@/views/nested/menu1/menu1-2/menu1-2-2'),
name: 'Menu1-2-2',
meta: { title: 'Menu1-2-2' }
}
]
},
{
path: 'menu1-3',
component: () => import('@/views/nested/menu1/menu1-3'),
name: 'Menu1-3',
meta: { title: 'Menu1-3' }
}
]
}, ]
},
{
path: 'external-link',
component: Layout,
children: [
{
path: 'https://panjiachen.github.io/vue-element-admin-site/#/',
meta: { title: 'External Link', icon: 'link' }
}
]
},
{
path: '/self',
component: Layout,
children: [
{
path: 'index',
name: 'self',
component: () => import('@/views/self'),
meta: { title: 'self', icon: 'form' }
}
]
}, // 404页面必须放在最后!!
// { path: '*', redirect: '/404', hidden: true }
] // 创建路由
const createRouter = () => new Router({
// mode: 'history', // 需要服务支持
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
}) var router = createRouter() // 重置路由
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
} //异步挂载的路由
//动态需要根据权限加载的路由表
export const asyncRouterMap = [
{
path: '/permission',
component: Layout,
name: 'permission',
redirect: '/permission/index222',
meta: {title:'permission', role: ['admin','super_editor'] }, //页面需要的权限
children: [
{
path: 'index222',
component: () => import('@/views/self'),
name: 'index222',
meta: {title:'权限测试1',role: ['admin','super_editor'] } //页面需要的权限
},
{
path: 'index333',
component: () => import('@/views/self'),
name: 'index333',
meta: {title:'权限测试2',role: ['admin','super_editor'] } //页面需要的权限
}
]
},
{ path: '*', redirect: '/404', hidden: true }
]; export default router

注意以上代码中红色的代码,这个routes中分两块路由配置,一块是固定的,无权限的路由配置,也就是不管是管理员身份,还是超级管理员身份,都会显示的路由配置。

第二块是,带权限的路由配置,根据用户权限来显示侧边栏。注意,带权限的配置里的meta中都有role项,代表是权限

首先,我们在获取用户信息时,会得到这个用户有哪些权限,是一个数组,假如是这样的

commit('SET_ROLES',['admin','super_editor']);//自定义权限

这个用户的权限有这些(admin、super_editor),然后再根据用户权限来筛选出符合的动态添加的路由,

什么时候筛选呢?

这就用到登录时的拦截器了,上面遇到过,就在哪执行,来看看那里都是写了一些什么代码:

拿到这看看:

          // 获得用户信息
await store.dispatch('user/getInfo');
//实际是请求用户信息后返回,这里是模拟数据,直接从store中取
const roles=store.getters.roles;
store.dispatch('permission/GenerateRoutes', { roles }).then(() => { // 生成可访问的路由表
router.addRoutes(store.getters.addRouters); // 动态添加可访问路由表
router.options.routes=store.getters.routers;
next({ ...to, replace: true });// hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
})

routes其实就是上面的两个权限组成的数组,然后传入了GenerateRoutes方法内,(注意es6语法,看不懂的去了解一下es6),再看看GenerateRoutes中的代码:

import { asyncRouterMap, constantRoutes } from '@/router';

function hasPermission(roles, route) {
if (route.meta && route.meta.role) {
return roles.some(role => route.meta.role.indexOf(role) >= 0)
} else {
return true
}
} const permission = {
namespaced: true,
state: {
routers: constantRoutes,
addRouters: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers;
state.routers = constantRoutes.concat(routers);
}
},
actions: {
GenerateRoutes({ commit }, data) {//roles是用户所带的权限
return new Promise(resolve => {
const { roles } = data;
const accessedRouters = asyncRouterMap.filter(v => {
// if (roles.indexOf('admin') >= 0) {
// return true;
// };
if (hasPermission(roles, v)) {
if (v.children && v.children.length > 0) {
v.children = v.children.filter(child => {
if (hasPermission(roles, child)) {
return child
}
return false;
});
return v
} else {
return v
}
}
return false;
});
commit('SET_ROUTERS', accessedRouters);
resolve();
})
}
}
}; export default permission;
GenerateRoutes方法办了一件事,就是把动态路由配置里符合用户权限的配置筛选出来,组成一个数组,然后,和固定路由合并到了一起,存到了vuex中,

然后调用了这两句代码:

          router.addRoutes(store.getters.addRouters); // 动态添加可访问路由表
router.options.routes=store.getters.routers;
router.addRoutes()方法是,动态添加路由配置,参数是符合路由配置的数组,
然后将路由元信息,变成合并后的路由元信息,因为渲染侧边栏需要用到,
这两句代码,顺序不能变,缺一不可,缺了addRoutes,点击侧边栏的动态路由会无反应,缺了options,侧边栏显示不出来动态添加的路由!!!! 以上就是element-admin的动态路由了,睡觉。。。。 今天正式开始用这个模板开发了,想说一下,这个模板有许多模块,我用不到,比如说mock文件,当我们不用它本身的api去请求接口时,完全可以将它删除,我是今天把它原有的登录逻辑去掉了,登录时,就不会有mock的事了。
于是,我一个一个文件去注释,对运行不影响的都删除了,test文件一下删除了,还有些不知道是干什么的js也去了,这样项目看着对我来说比较轻巧,也比较熟悉。另外,哪些utils。还有store里不用的方法都可以去掉。
当我把登录改了之后,能去掉一大堆东西好不好。ok,有时间整理一个轻巧的模板,挂到码云上 更新一个bug,目前页面上的路由拦截器的逻辑,并不是我现在用的逻辑,不过相差不远,项目上线后,发现刷新动态加载的路由页面,页面白屏,调试一下,发现是路由拦截器在重复跳转页面,这问题很奇怪,
本地没有问题,然后琢磨了半天,既然是重复跳转,它有一个特征就是:to.path==from.path;那么可以利用这一特质,去拦截这种情况,
if(to.path==from.path){
next();
return;
}

监听浏览器前进后退事件

window.addEventListener("popstate", function () {
backStatus=true;
return;
})

最新文章

  1. selenium 关于富文本的处理
  2. HDU 1575
  3. 使用nbrbutil工具來處理requested media id is in use, cannot process request
  4. win7 创建软链接方式
  5. List, Set, Map是否继承自Collection接口?
  6. jython学习笔记3
  7. GitCam一款Gif动画制作软件
  8. (转)Libevent(2)— event、event_base
  9. “ORA-12545: 因目标主机或对象不存在,连接失败”怎么办?
  10. Float之谜
  11. Oracle执行计划——使用index full scan的几种情况
  12. UC/OS操作系统 (转)
  13. js中constructor的作用
  14. openssl ans.1编码规则分析及证书密钥编码方式
  15. HDU 1796 容斥原理 How many integers can you find
  16. HANA CDS与ABAP CDS
  17. HighCharts之2D堆柱状图
  18. js常用正则表达式表单验证代码
  19. 并发编程(四):atomic
  20. 测试之法 —— mock object

热门文章

  1. Ubuntu 安装 QtCreator (version : Qt 5.9.8)
  2. HeidiSQL 导入Excel数据
  3. windows下安装mongoDB(zip版)
  4. IntelliJ IDEA 搭建 Go 开发环境
  5. [Tarjan系列] Tarjan算法求无向图的桥和割点
  6. 【死磕Java并发】—–深入分析ThreadLocal
  7. Tag Helper1
  8. Activiti使用总结
  9. vue使用sass报Modele build failed: TypeError: this.getResolve is not a function at Object.loader...
  10. 阿里十年架构师告诉你Spring Boot与Spring Cloud是什么关系