讀vue-element-admin原始碼二三事(一)
阿新 • • 發佈:2019-01-10
因為本人是前端純菜鳥,除了上週大概看了vue.js較簡單的部分以及簡單的ajax和js基礎框架什麼接觸的少,其他沒接觸過,以下沒接觸過的框架用單行程式碼表示,如123
文章目錄
基礎
登入
- 路由
看著路由帶#還是很不舒適,於是router/index.js內的
export default new Router({
mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRouterMap
})
mode:histroy部分放開去#號
- URL過濾
main.js內匯入了permission.js,程式碼如下:
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
if (getToken()) { // determine if there has token
/* has token*/
if (to.path === '/login') {
next({ path: '/' })
NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it
} else {
if (store.getters.roles.length === 0) { // 判斷當前使用者是否已拉取完user_info資訊
store.dispatch('GetUserInfo').then(res => { // 拉取user_info
const roles = res.data.roles // note: roles must be a array! such as: ['editor','develop']
store.dispatch('GenerateRoutes', { roles } ).then(() => { // 根據roles許可權生成可訪問的路由表
router.addRoutes(store.getters.addRouters) // 動態新增可訪問路由表
next({ ...to, replace: true }) // hack方法 確保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
})
}).catch((err) => {
store.dispatch('FedLogOut').then(() => {
Message.error(err || 'Verification failed, please login again')
next({ path: '/' })
})
})
} else {
// 沒有動態改變許可權的需求可直接next() 刪除下方許可權判斷 ↓
if (hasPermission(store.getters.roles, to.meta.roles)) {
next()
} else {
next({ path: '/401', replace: true, query: { noGoBack: true }})
}
// 可刪 ↑
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) { // 在免登入白名單,直接進入
next()
} else {
next(`/login?redirect=${to.path}`) // 否則全部重定向到登入頁
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
}
}
})
此處用到框架:NProgress.js
大概看了一下,是個進度條。
最外層虛擬碼:
如果(有token【已登入】){
。。。
}否則{
如果(當前跳轉地址在白名單【此地址不需要登入允許匿名訪問】){
直接進入
}否則{
回登入頁
}
}
由此攔截未登入的使用者到登入頁
- 角色管理
繼續看beforeEach部分,會發現登入後,有一個拉去使用者資料,然後生成路由的特殊部分,用了vuex的store
,內部根據asyncRouterMap和當前角色(目前有admin和editor[editor需過濾])進行路由篩選,實際是檢查路由的meta->roles欄位是否包含當前角色,包含就放入路由中。主要通過下列兩段程式碼完成:
src/store/modules/permission.js
/**
* 通過meta.role判斷是否與當前使用者許可權匹配
* @param roles
* @param route
*/
function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
/**
* 遞迴過濾非同步路由表,返回符合使用者角色許可權的路由表
* @param routes asyncRouterMap
* @param roles
*/
function filterAsyncRouter(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRouter(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
- 多語言
用到框架:vue-i18n
具體用法見src/components/LangSelect/index.vue
<template>
<el-dropdown trigger="click" class="international" @command="handleSetLanguage">
<div>
<svg-icon class-name="international-icon" icon-class="language" />
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :disabled="language==='zh'" command="zh">中文</el-dropdown-item>
<el-dropdown-item :disabled="language==='en'" command="en">English</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
computed: {
language() {
return this.$store.getters.language
}
},
methods: {
handleSetLanguage(lang) {
this.$i18n.locale = lang
this.$store.dispatch('setLanguage', lang)
this.$message({
message: 'Switch Language Success',
type: 'success'
})
}
}
}
</script>
主頁
程式碼:
{
path: '',
component: Layout,
redirect: 'dashboard',
children: [
{
path: 'dashboard',
component: () => import('@/views/dashboard/index'),
name: 'Dashboard',
meta: { title: 'dashboard', icon: 'dashboard', noCache: true }
}
]
},
看路由是Layout裡面嵌套了一個<router-view/>
裡面放了主面板,看程式碼是這樣:
<template>
<div :class="classObj" class="app-wrapper">
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
<sidebar class="sidebar-container"/>
<div class="main-container">
<navbar/>
<tags-view/>
<app-main/>
</div>
</div>
</template>
就是側邊欄+主欄,主欄包括導航欄,標籤欄和內容。
- 側邊欄
<template>
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:show-timeout="200"
:default-active="$route.path"
:collapse="isCollapse"
mode="vertical"
background-color="#304156"
text-color="#bfcbd9"
active-text-color="#409EFF"
>
<sidebar-item v-for="route in permission_routers" :key="route.name" :item="route" :base-path="route.path"/>
</el-menu>
</el-scrollbar>
</template>
這裡如果沒有看路由過濾是會有點費解的。通過路由過濾動態設定路由,然後再把路由傳到這裡,然後看sidebar-item內部:
<template>
<div v-if="!item.hidden&&item.children" class="menu-wrapper">
<!--單根直接連結,多個下拉列表-->
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
<a :href="onlyOneChild.path" target="_blank" @click="clickLink(onlyOneChild.path,$event)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
<item v-if="onlyOneChild.meta" :icon="onlyOneChild.meta.icon||item.meta.icon" :title="generateTitle(onlyOneChild.meta.title)" />
</el-menu-item>
</a>
</template>
<el-submenu v-else :index="item.name||item.path">
<template slot="title">
<item v-if="item.meta" :icon="item.meta.icon" :title="generateTitle(item.meta.title)" />
</template>
<template v-for="child in item.children" v-if="!child.hidden">
<sidebar-item
v-if="child.children&&child.children.length>0"
:is-nest="true"
:item="child"
:key="child.path"
:base-path="resolvePath(child.path)"
class="nest-menu" />
<a v-else :href="child.path" :key="child.name" target="_blank" @click="clickLink(child.path,$event)">
<el-menu-item :index="resolvePath(child.path)">
<item v-if="child.meta" :icon="child.meta.icon" :title="generateTitle(child.meta.title)" />
</el-menu-item>
</a>
</template>
</el-submenu>
</div>
</template>
其實不是很複雜,就是如果當前根路由沒有hidden,並且有子路由,分兩種情況處理:
1.單個子路由或除了一個子路由其他全部隱藏
例如:
{
path: '',
component: Layout,
redirect: 'dashboard',
children: [
{
path: 'dashboard',
component: () => import('@/views/dashboard/index'),
name: 'Dashboard',
meta: { title: 'dashboard', icon: 'dashboard', noCache: true }
}
]
}
就是首頁,那麼點選標題直接進行連結。
2.多個子路由開啟下拉列表,下拉列表再進行連結
例如:
{
path: '/permission',
component: Layout,
redirect: '/permission/index',
alwaysShow: true, // will always show the root menu
meta: {
title: 'permission',
icon: 'lock',
roles: ['admin', 'editor'] // you can set roles in root nav
},
children: [
{
path: 'page',
component: () => import('@/views/permission/page'),
name: 'PagePermission',
meta: {
title: 'pagePermission',
roles: ['admin'] // or you can only set roles in sub nav
}
},
{
path: 'directive',
component: () => import('@/views/permission/directive'),
name: 'DirectivePermission',
meta: {
title: 'directivePermission'
// if do not set roles, means: this page does not require permission
}
}
]
}
今天到這裡了,剩下時間打算看看上述的幾個框架vuex store
,element ui
和vue-i18n
,還不太會用。