vue-element-admin 登陸


vue-element-admin 登陸

引言

vue-element-admin是vue生態圈中,最火的一個後臺管理框架。基於vue和element-ui實現。

這篇文章主要會講解登陸的流程以及我認為這個框架的厲害的東西:動態路由,之前看代碼的時候,總想著一個登陸搞那麼麻煩,後面仔細品味發現原來一個小小的登陸功能涉及到了這麼多的東西。

準備工作

目錄結構

瞭解一個框架之前,先要從目錄結構入手(這裡直接引用花褲衩大佬的目錄結構)。目錄結構的瞭解能夠更加清楚模塊功能的劃分。

<code>├── src                        # 源代碼
│ ├── api # 所有的api請求
│ ├── assets # 主題 字體等靜態資源
│ ├── components # 全局公用組件
│ ├── directive # 全局指令
│ ├── filters # 全局 filter過濾器
│ ├── icons # 項目所有 svg icons
│ ├── layout # 全局 layout
│ ├── router # 路由
│ ├── store # 全局 store管理
│ ├── styles # 全局樣式
│ ├── utils # 全局公用方法

│ ├── vendor # 公用vendor
│ ├── views # views 所有頁面
│ ├── App.vue # 入口頁面
│ ├── main.js # 入口文件 加載組件 初始化等
│ └── permission.js # 權限管理
├── tests # 測試
├── .env.xxx # 環境變量配置
├── .eslintrc.js # eslint 配置項
├── .babelrc # babel-loader 配置
├── .travis.yml # 自動化CI配置
├── vue.config.js # vue-cli 配置
├── postcss.config.js # postcss 配置
└── package.json # package.json
複製代碼/<code>

permission.js

在登陸這個流程中,permission.js這個是最重要的一環,其實這個文件就是路由的全局鉤子(beforeEach和afterEach),全局鉤子的意思就是每次跳轉的時候可以根據情況進行攔截,不讓它進行跳轉。使用場景最常見的就是有些頁面需要用戶登陸之後才能訪問,就可以在beforeEach中校驗用戶是否登陸來進行相對應的攔截處理。下面會詳細的講解permission.js的內容。

util / auth.js

這個文件主要就是設置token到cookie中的操作封裝。

router

這個是路由中的一些設置,理解這個後面看組件Sidebar、TagViews將會事半功倍。

<code>/**
hidden: true 是否隱藏於Sidebar側邊欄
alwaysShow: true\t\t\t\t\t是否顯示在根菜單
redirect: noRedirect\t\t\t\tBreadcrumb中重定向的path
name: 'router-name'\t\t\t\t用於keep-alive的Name
meta: {
\troles: ['admin', 'editor'],\t\t當前路由的訪問所需要權限
\ttitle: 'title',\t\t\t\t\tSidebar和Breadcrumb的title
\ticon: 'svg-name',\t\t\t\tSibebar的icon
\tnoCache: true\t\t\t\t\t是否設置不緩存
\tbreadcrumb: true\t\t\t\t是否顯示在Breadcrumb上
\tactiveMenu: '/example/list'\t\tSidebar高亮時的顯示path
}
**/
複製代碼/<code>

其他的一些我就沒有介紹了,比如說封裝好axios的request.js,還有把請求封裝成api,這些可以自行去了解。

view / login / index.vue

省略了一些的細枝末節,直接從點擊登陸之後發生了一系列事情開始講起,第一個就是handleLogin方法。

<code>handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
this.$store.dispatch('user/login', this.loginForm)
.then(() => {
this.$router.push({
path: this.redirect || '/',
query: this.otherQuery
})
this.loading = false
})
.catch(() => {
this.loading = false

})
} else {
console.log('error submit!!')
return false
}
})
}
複製代碼/<code>

可以看到這個方法很簡單,就是利用validate方法進行表單驗證,驗證通過則使用this.$store.dispatch調用user/login方法並傳遞這個表單的數據,然後有一個.then()方法,方法裡面有this.$router.push(...),可能有同學就會有疑惑了,this.redirect和this.otherQuery是啥,用一句話來概括就是:我從哪裡跳到/login頁面,登陸之後我就返回到哪裡。

這個user/login是什麼呢?一起來揭開它神秘的面紗。

user / login

先解析這個之前,先來補充一點vuex基礎知識:

vuex中使用namespaced:true開啟命名空間,調用mutations或者調用actions,則是模塊名 + 相對應的方法名。

另外actions是異步的,action處理函數之後返回的Promise進行相對應的處理。

<code>// user.js
// 不開啟命名空間
const actions = {
login(){}
}
export default { actions };

this.$store.dispatch('login');


// user.js
// 開啟命名空間
const actions = {
login(){}
}
export default { actions, namespaced: true }; // 注意!開啟命名空間

this.$store.dispatch('user/login'); // 模塊名user + 方法名 login
複製代碼/<code>

上面所說的user/login,則就是user模塊中的login方法,核心代碼就如下:

<code>login({ commit }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password }).then(response => {
const { data } = response // 解構出data
commit('SET_TOKEN', data.token) // 更新store裡面的token
setToken(data.token) // token保存到cookie
resolve()
}).catch(error => {
reject(error)
})
})
}
複製代碼/<code>

一句話來概括:登陸驗證,登陸成功之後,分別保存token到vuex、cookie中。

這裡完成之後就會回到之前的view/login/index.vue,當user/login調用完成之後,則會進行.then(...)方法,就是一個路由跳轉的過程(this.$router.push(...))。

Permission.js

這個文件就如同前面介紹所說,路由的全局鉤子,

動態路由的實現這裡相當於是一個入口。來看核心的實現代碼。

<code>router.beforeEach(async(to, from, next) => {
// 從cookie中取得token
const hasToken = getToken()

// 如果有token 也就是已經登陸的情況下
if (hasToken) {
// 並且要前往的路徑是'/login' 則返回 '/'
if (to.path === '/login') {
next({ path: '/' })
} else {
// 從store中取得用戶的 roles, 也就是用戶的權限 並且用戶的權限數組必須有一個以上
const hasRoles = store.getters.roles && store.getters.roles.length > 0
// 有權限則直接進入
if (hasRoles) {
next()
} else {
// 沒有權限的話
try {
// 獲取用戶信息
const { roles } = await store.dispatch('user/getInfo')
\t\t // 生成可訪問路由
const accessRoutes = await store.dispatch('permission/generateRoutes', \t\t\t \t\t\t\t\t\t\t\t\t\t\t\t\t\troles)
// 將可訪問路由添加到路由上
router.addRoutes(accessRoutes)
// 進入路由
next({ ...to, replace: true })
} catch (error) {
// 如果出現異常 清空路由
await store.dispatch('user/resetToken')
// Message提示錯誤
Message.error(error || 'Has Error')
// 跳到login頁面重新登陸

next(`/login?redirect=${to.path}`)
}
}
}
} else {
// 沒有token 也就是沒有登陸的情況下
// 判斷是否是白名單(也就是說不需要登陸就可以訪問的路由)
if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
// 其他的一路給我跳到login頁面 老老實實的進行登陸
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
複製代碼/<code>

註釋已經寫的明明白白了,這個思路其實使用的特別多,就是利用全局鉤子進行訪問的攔截,如果沒有登陸的話,跳轉到登陸頁面進行登陸。

但是花褲衩大佬的這個有一點點不同,可以看到他將登陸和獲取用戶信息分成了兩步,原因就是保證用戶信息是最新的。第二個是在獲取用戶信息之後返回的roles生成可訪問的路由,也就是這兩步實現了動態路由。

用一句話來概括:是否登陸?沒有就給我老老實實登陸。是否有用戶信息?沒有就給我獲取用戶信息,並且生成可訪問路由然後利用addRoutes進行添加。

這兩步都是actions:user/getInfo,permission/generateRoutes。

可以看到這是vuex中的actions提交,可能有些小夥伴會有點困惑,為什麼有的請求直接寫在api文件夾,有些就放在actions裡面?這個問題:我的理解就是,如果返回的數據要保存在vuex中的話,可以直接使用actions,原因是action裡面可以提交mutation改變store的狀態,可以少寫一些代碼,同時思路更加清晰,如果返回的數據只需要在當前頁面使用的話,並不需要保存到vuex中,那就直接用寫在api文件夾中引用即可。當然這個是我的個人理解,如有錯誤請望指出。

user / getInfo

首先我們看第一個user/getInfo。

如上面所說,這是user模塊中的getInfo方法,來看核心代碼。

<code>getInfo({ commit , state }) {
return new Promise((resolve, reject) => {
// 調用getInfo接口
getInfo(state.token).then(response => {
const { data } = response // 解構出data
if (!data) { // 進行數據校驗
reject('Verification failed, please Login again.')
}
// 解構出需要保存的值
const { roles, name, avatar, introduction } = data
// roles權限數組至少有一個權限
if (!roles || roles.length <= 0) {
reject('getInfo: roles must be a non-null array!')

}
// 保存到vuex
commit('SET_ROLES', roles)
commit('SET_NAME', name)
commit('SET_AVATAR', avatar)
commit('SET_INTRODUCTION', introduction)
// 將 data 返回
resolve(data)
}).catch(error => {
reject(error)
})
})
}
複製代碼/<code>

這個就是調用getInfo接口獲取用戶信息並且保存到vuex中,為了嚴謹性,進行相對應的數據校驗,然後把data返回。

用一句話來概括:獲取用戶信息,並保存用戶信息。

permission/generateRoutes

我們來看第二個actions,這個是動態路由中的重要一步,生成可訪問路由,根據當前用戶的權限數組,和路由中可訪問的權限數組,進行匹配生成。

<code>// 判斷是否有權限
function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
// roles.some => Array.some 相當於是隻要有一個滿足就為true

// 判斷用戶的權限於當前路由訪問所需要的權限是否有一個滿足
// 比如說用戶權限為 ['one','two'] 當前路由訪問所需要權限為 ['two','three'] 那麼就說明當前用戶可以訪問這個路由

return roles.some(role => route.meta.roles.includes(role))
} else {
// 默認是可訪問的
return true
}
}
// 生成可訪問路由
export function filterAsyncRoutes(routes, roles) {
const res = []

routes.forEach(route => {
const tmp = { ...route }
// 判斷當前路由是否可以訪問
if (hasPermission(roles, tmp)) {
// 如果當前路由還有子路由
if (tmp.children) {
// 進行遞歸處理
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
// 將可訪問路由放入數組中
res.push(tmp)
}
})
// 返回
return res
}

// 為什麼要寫這裡呢,因為後面的Sidebar組件與這個環環相扣
const mutations = {
SET_ROUTES: (state, routes) => {
// 添加的路由
state.addRoutes = routes
// 將vuex中的路由進行更新
state.routes = constantRoutes.concat(routes)
}
}

const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
let accessedRoutes

// 如果roles包含 'admin' 直接可以全部訪問
if (roles.includes('admin')) {
accessedRoutes = asyncRoutes || []
} else {
// 利用 filterAsyncRoutes 過濾出可訪問的路由
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
// 保存可訪問的路由到store中
commit('SET_ROUTES', accessedRoutes)
// 將可訪問路由返回
resolve(accessedRoutes)
})
}
}
複製代碼/<code>

歐克,註釋已經全部寫好了,這個就是動態路由中重要一步,慢慢品味發現也不難,越看越覺得666。

用一句話來概括:根據得到的用戶權限生成可以訪問的路由。

動態路由的實現

<code>router.addRoutes(accessRoutes)

複製代碼/<code>

???,沒呢?

對的!動態路由的核心代碼就這一句話,短小精悍,其他的都是為了完成動態路由做的一些 "準備工作" ,user/getInfo獲取用戶信息得到用戶的roles權限數組,返回user/generateRoutes生成可訪問路由,就是這麼神奇的一步,實現了動態路由。

寫到這裡基本流程就走完了,但是還有兩個注意點。

注意

為什麼項目開啟默認就是登陸頁面

想必通過之前的講解也應該知道了permission.js的作用了,全局路由鉤子,每次路由跳轉都要調用全局路由鉤子(誰讓它是全局的呢),原因就是當頁面加載首頁時也會經過全局路由鉤子,而permission.js判斷當前用戶有沒有登陸,沒有登陸就直接跳轉到/login頁面進行登陸把,就是這麼任性,啦啦啦。

動態路由顯示不出來

有些小夥伴可能會改這個框架的代碼,但是發現顯示不出來?這也是我遇到過的一個坑,需要注意的是permission.js裡面有兩個條件:第一個是否登陸?第二個是否獲取用戶信息(判斷是否獲取用戶信息是根據roles)?有些小夥伴可能是登陸的時候就把用戶信息獲取了,但是!!沒有更新用戶roles權限數組,所以就一直會獲取用戶信息,從而導致顯示不出來。

完結,撒花

其實到這裡整個登陸流程就已經結束了,可以看到花褲衩大佬想的很多,用戶信息的獲取和用戶登陸的分離,根據用戶權限生成可以訪問的路由並添加,這些思路我覺得很厲害,還有這麼多組件可以用,哈哈哈哈。

好的,我們來總結一下:

  1. Login/index.vue點擊登陸 提交user/login的actions。
  2. user/login進行登陸驗證,登陸成功之後保存token到vuex和cookie中。
  3. 回到Login/index.vue跳轉路由,這時就到了permission.js
  4. permission.js進行判斷是否登陸,是否有用戶信息?沒有用戶信息就獲取用戶信息,並且保存到vuex,然後根據用戶信息中的roles生成可訪問路由,並通過addRoutes進行添加。

看到這裡,相信你對這個框架的登陸流程已經有一定的瞭解了,自己再去理一遍把,啦啦啦啦,那下面這一張流程圖相信你也可以看懂。

vue-element-admin 登陸


分享到:


相關文章: