案例: vue-element-asyncLoginjavascript
優化流程,簡化邏輯,去除_import.js文件,改成直接動態導入css
優化404頁面前端
更新爲最新的vue-element-template的模板vue
增長思路說明java
優化部分代碼git
增長QQ交流羣,有問題請加羣程序員
在今年年初在掘金髮布了一篇文章記一次Vue動態渲染路由的實現,如今代碼通過不斷的Reviewgithub
如今徹底優化了以前的實現方法,代碼量減小不少,邏輯更加簡單,同時也更加穩定vue-router
demo已經部署到github,歡迎體驗~~ vue-element-asyncLogin, 你的start是個人動力!vuex
前端路由鑑權相信只要瞭解過vue-element-admin的都知道,前端鑑權方案是徹底可行的,思路清晰,難度適中,項目中徹底可使用,那麼相對來講動態路由優點在什麼地方呢
前端鑑權
明顯更加好用,成本更低,程序員們也不用996了(霧),可是對於權限等級不少,而且比較大的項目,維護這一套鑑權路由,毫無疑問是一個大工程,而且面對頻繁變動的需求,bug會出現的更加頻繁,前端工程師工做量大大增長,這時候彷佛前端鑑權就再也不是好的方案
相比較以前使用localStorage存儲登陸狀態,如今把登陸狀態交給cookice進行管理
路由信息所有交給Vuex進行管理,再也不從localStorage裏面走,增長了系統的穩定性
配置基礎路由
具體的實現思路
router/router.js
// ...... // 靜態路由 export const StaticRouterMap = [ { path: '/login', component: login, meta: { title: '管理員登陸' }, hidden: true }, { path: '/user', component: userLogin, redirect: '/user/userlogin', name: 'user', hidden: true, children: [ { path: 'userLogin', component: () => import('@/views/userLogin/components/login'), meta: { title: '商戶登陸' } }, { path: 'userRegistry', component: () => import('@/views/userLogin/components/registry'), meta: { title: '商戶註冊' } } ] }, { path: '/', component: Layout, redirect: '/dashboard', name: 'dashboard', children: [ { path: 'dashboard', component: () => import('@/views/dashboard/index'), meta: { title: '根目錄', icon: 'dashboard', affix: true } } ] }, { path: '/404', component: () => import('@/views/404'), hidden: true } ] export default new Router({ mode: 'history', scrollBehavior: () => ({ y: 0 }), routes: StaticRouterMap }) 複製代碼
與後端同窗定製路由結構 (如下爲json)
後端會根據當前用戶權限動態返回路由結構 前端再也不須要考慮權限問題
[{
id: 1,
name: 'Example',
code: null,
description: null,
url: '/example',
component: 'layout',
generatemenu: 1,
sort: 0,
parentId: null,
permName: null,
redirect: '/example/table',
title: '普通用戶',
icon: 'example',
children: [
{
id: 2,
name: 'Table',
code: null,
description: null,
url: 'table',
component: 'table',
generatemenu: 1,
sort: 0,
parentId: 1,
permName: null,
redirect: '',
title: 'Table',
icon: 'table',
children: null
},
{
id: 3,
name: 'Tree',
code: null,
description: null,
url: 'tree',
component: 'tree',
generatemenu: 1,
sort: 0,
parentId: 1,
permName: null,
redirect: '',
title: 'Tree',
icon: 'tree',
children: null
}
]
}]
複製代碼
處理後端原始路由數據
../utils/addRouter
遞歸寫入比以前版本的遞歸刪除更加穩定,代碼量也更少
注意,路由結構與目錄結構是對應的
/** * 生成路由 * @param {Array} routerlist 格式化路由 * @returns */ export function addRouter(routerlist) { const router = [] try { routerlist.forEach(e => { let e_new = { path: e.url, name: e.name, component: () => e.component === 'layout' ? import('@/layout') : import(`@/views/${e.component}/index`) } if (e.children) { const children = addRouter(e.children) // 保存權限 e_new = { ...e_new, children: children } } if (e.redirect) { e_new = { ...e_new, redirect: e.redirect } } if (e.generatemenu === 0) { e_new = { ...e_new, hidden: true } } if (e.icon !== '' && e.title !== '') { e_new = { ...e_new, meta: { title: e.title, icon: e.icon } } } else if (e.title !== '' && e.icon === '') { e_new = { ...e_new, meta: { title: e.title } } } router.push(e_new) }) } catch (error) { console.error(error) return [] } return router } 複製代碼
處理後的路由
咱們處理後的路由後面須要與現有的router進行拼接,這裏須要根據需求 修改處理路由的規則
以上的都是準備工做,就是爲了將初始路由
與後端返回的動態路由
進行拼接
這部分代碼也是優化的核心
import router from './router' import store from './store' import user from './store/modules/user' import { getToken, removeToken } from './utils/auth' import NProgress from 'nprogress' // Progress 進度條 import 'nprogress/nprogress.css' // Progress 進度條樣式 import { Message } from 'element-ui' import { getRouter } from './api/login' import { addRouter } from './utils/addRouter' const whiteList = ['/login'] router.beforeEach((to, from, next) => { NProgress.start() if (getToken()) { // 判斷cookice是否存在 不存在即爲未登陸 if (to.path !== '/login') { if (user.state.init) { // 獲取了動態路由 data必定true,就無需再次請求 直接放行 next() } else { // data爲false,必定沒有獲取動態路由,就跳轉到獲取動態路由的方法 gotoRouter(to, next) } } else { Message({ message: '您已經登陸', type: 'info' }) next('/') } } else { if (whiteList.indexOf(to.path) !== -1) { // 免登錄白名單 直接進入 next() } else { if (to.path !== '/login') { // 重定向到登陸頁面 不能這麼寫 由於假如以前的角色是 管理員頁面 後又登錄了非管理員 重定向的頁面就可能不存在,就會致使404 // next(`/login?redirect=${to.path}`) next('/login') } else { next() } } } }) router.afterEach((to, from) => { NProgress.done() // 結束Progress }) function gotoRouter(to, next) { getRouter(store.getters.token) // 獲取動態路由的方法 .then(res => { console.log('解析後端動態路由', res) const asyncRouter = addRouter(res.data.router) // 進行遞歸解析 store.dispatch('user/setroles', res.data.permit) return asyncRouter }) .then(asyncRouter => { router.addRoutes(asyncRouter) // vue-router提供的addRouter方法進行路由拼接 console.log(asyncRouter) store.dispatch('user/setRouterList', asyncRouter) // 存儲到vuex store.dispatch('user/GetInfo') store.commit('user/set_init', true) next({ ...to, replace: true }) // hack方法 確保addRoutes已完成 }) .catch(e => { console.log(e) removeToken() }) } 複製代碼
Vuex內部的邏輯
import { StaticRouterMap } from '../../router/index' state: { //..... RouterList: [] // 動態路由 }, mutations: { set_router: (state, RouterList) => { state.RouterList = RouterList } }, action: { // 動態設置路由 此爲設置設置途徑 setRouterList({ commit }, routerList) { commit('set_router', StaticRouterMap.concat(routerList)) // 進行路由拼接並存儲 }, } 複製代碼
相對以前的邏輯要簡單不少
須要注意的是 經過 addRoutes合併的路由 不會被this.$router.options.routes
獲取到,因此須要將獲取的路由拼接到this.$router.options.routes
上
最後修改渲染側邊欄部分部分的代碼
src\views\layout\components\Sidebar\index.vue
computed: { // .... routes() { return this.$store.getters.routerList }, // .... } 複製代碼
我已精心準備了一個簡單的demo vue-element-asyncLogin,歡迎體驗,若是對你有幫助,請不要吝嗇你的start~~1