vue項目前端鑒權方式常用的有以下三種:
1、渲染菜單時控制模塊按鈕的顯示隱藏(不足:直接輸入鏈接仍然可以訪問模塊)
2、在路由導航守衛中攔截,針對沒有權限的模塊進行重定向(不足:每次訪問模塊都需要鑒定權限,模塊數量過多時會影響系統性能)
3、借助vue-router 2.x版本新加的API addRouters動態添加路由信息(不足:首次加載需要解析和添加,多跳轉一次路由)
綜上所述,權衡之后選擇了addRoutes動態添加,首屏加載時間可能會多出0.5s左右,加載一次之后后續就不需要再進行處理,可以提升系統的可靠性與穩定性。具體使用如下:
1、定義固定路由,用於路由初始化,如:登錄頁、404頁面等
const router = new Router({ // mode: 'history', // base: base, routes: [ { path: '/', name: '', component: () => import('@/views/login/login') , meta:{label: '登錄'} } ], scrollBehavior(to, from, savedPosition) { if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } } });
2、路由導航守衛前置攔截
為了方便,將路由權限信息保存到vuex中,在路由跳轉時,判斷state中是否存在menu信息,如果不存在,則向后端請求權限信息,此部分需要阻塞頁面的跳轉,改為同步執行;
針對一般菜單嵌套路由,需要對路由信息最好進行扁平化處理,否則可能導致路由重復添加或者添加失敗(上級有父組件會導致重復添加,上級沒有組件單純嵌套會導致添加失敗)
//刷新頁面后,會導致動態生成的路由失效,需要重新走下添加路由操作 let registerRouteRefresh = true; router.beforeEach(async (to, from, next) => { //如果不存在菜單信息,則走動態授權 if (store.state.menus.length === 0) { //確保初始化信息完成后才會執行下一步動作 await init(); let nodesList = []; //遞歸獲取權限菜單列表 initNodes(menuNodes, nodesList); store.commit('UPDATE_DATA', { key: 'menus', value: nodesList }); //獲取路由權限信息 const asyncNodes = getAsyncNodes(nodesList, []); //添加擁有權限的路由信息 router.addRoutes(asyncNodes); registerRouteRefresh = false; next({ ...to, replace: true }) }else { //如果是頁面刷新,需要重新加載下動態路由 if (registerRouteRefresh) { let nodesList = []; //遞歸獲取權限菜單列表 initNodes(menuNodes, nodesList); //獲取路由權限信息 const asyncNodes = getAsyncNodes(nodesList, []); //添加擁有權限的路由信息 router.addRoutes(asyncNodes); registerRouteRefresh = false; //確保路由加載完成 next({ ...to, replace: true }) } next() } });
注意:
1、由於路由時動態添加的,存儲在內存中,頁面刷新之后內存中變量也會消失,動態添加的路由也會隨之消失,所以每次刷新頁面需要重新走一遍添加路由的流程
2、由於路由是動態添加的,在路由跳轉時,添加的路由並沒有生效,所以還需要多跳轉一次頁面
除此之外,按鈕的鑒權可以通過自定義指令,動態傳入條件參數來實現按鈕的顯示隱藏,指令封裝如下:
/** * 節點鑒權 * {code: menuCode, flag:條件生效取反} */ Vue.directive('permission', { bind(el, binding) { let vv = binding.value; if (vv.flag) { el.style.display = hasPermission(vv.code) ? 'none' : 'block'; } else { el.style.display = hasPermission(vv.code) ? 'block' : 'none'; } } });
使用方法:
<button v-permission="{code: 123}"></button >