vue實現動態路由詳細
主流的實現方式:
簡單聊一下兩種方式的優勢,畢竟如果你從來沒做過,說再多也看不明白,還是得看代碼
前端控制
- 不用後端幫助,路由表維護在前端
- 邏輯相對比較簡單,比較容易上手
後端控制
- 相對更安全一點
- 路由表維護在數據庫
一、前端控制
思路:在路由配置裡,通過meta
屬性,擴展權限相關的字段,在路由守衛裡通過判斷這個權限標識,實現路由的動態增加,及頁面跳轉;如:我們增加一個role
字段來控制角色
具體方案:
1、根據登錄用戶的賬號,返回前端用戶的角色
2、前端根據用戶的角色,跟路由表的meta.role進行匹配
3、講匹配到的路由形成可訪問路由
核心代碼邏輯
1、在router.js文件(把靜態路由和動態路由分別寫在router.js)
import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) import Layout from '@/layout' // constantRoutes 靜態路由,主要是登錄頁、404頁等不需要動態的路由 export const constantRoutes = [ { path: '/redirect', component: Layout, hidden: true, children: [ { path: '/redirect/:path*', component: () => import('@/views/redirect/index') } ] }, { path: '/login', component: () => import('@/views/login/index'), hidden: true }, { path: '/404', component: () => import('@/views/error-page/404'), hidden: true }, { path: '/401', component: () => import('@/views/error-page/401'), hidden: true } ] // asyncRoutes 動態路由 export const asyncRoutes = [ { path: '/permission', component: Layout, redirect: '/permission/page', alwaysShow: true, name: 'Permission', meta: { title: 'Permission', icon: 'lock', // 核心代碼,可以通過配的角色來進行遍歷,從而是否展示 // 這個意思就是admin、editor這兩個角色,這個菜單是可以顯示 roles: ['admin', 'editor'] }, children: [ { path: 'page', component: () => import('@/views/permission/page'), name: 'PagePermission', meta: { title: 'Page Permission', // 這個意思就是隻有admin能展示 roles: ['admin'] } } ] } ] const createRouter = () => new Router({ scrollBehavior: () => ({ y: 0 }), routes: constantRoutes }) const router = createRouter() // 這個是重置路由用的,很有用,別看這麼幾行代碼 export function resetRouter() { const newRouter = createRouter() router.matcher = newRouter.matcher } export default router
2、store/permission.js(在vuex維護一個state,通過配角色來控制菜單顯不顯示)
import { asyncRoutes, constantRoutes } from '@/router' // 這個方法是用來把角色和route.meta.role來進行匹配 function hasPermission(roles, route) { if (route.meta && route.meta.roles) { 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 } const state = { routes: [], addRoutes: [] } const mutations = { SET_ROUTES: (state, routes) => { // 這個地方維護瞭兩個狀態一個是addRouters,一個是routes state.addRoutes = routes state.routes = constantRoutes.concat(routes) } } const actions = { generateRoutes({ commit }, roles) { return new Promise(resolve => { let accessedRoutes if (roles.includes('admin')) { accessedRoutes = asyncRoutes || [] } else { // 核心代碼,把路由和獲取到的角色(後臺獲取的)傳進去進行匹配 accessedRoutes = filterAsyncRoutes(asyncRoutes, roles) } // 把匹配完有權限的路由給set到vuex裡面 commit('SET_ROUTES', accessedRoutes) resolve(accessedRoutes) }) } } export default { namespaced: true, state, mutations, actions }
3、src/permission.js
(新建一個路由守衛函數,可以在main.js,也可以抽離出來一個文件)
這裡面的代碼主要是控制路由跳轉之前,先查一下有哪些可訪問的路由,登錄以後跳轉的邏輯可以在這個地方寫
// permission.js router.beforeEach((to, from, next) => { if (store.getters.token) { // 判斷是否有token if (to.path === '/login') { next({ path: '/' }); } else { // 判斷當前用戶是否已拉取完user_info信息 if (store.getters.roles.length === 0) { store.dispatch('GetInfo').then(res => { // 拉取info const roles = res.data.role; // 把獲取到的role傳進去進行匹配,生成可以訪問的路由 store.dispatch('GenerateRoutes', { roles }).then(() => { // 動態添加可訪問路由表(核心代碼,沒有它啥也幹不瞭) router.addRoutes(store.getters.addRouters) // hack方法 確保addRoutes已完成 next({ ...to, replace: true }) }) }).catch(err => { console.log(err); }); } else { next() //當有用戶權限的時候,說明所有可訪問路由已生成 如訪問沒權限的全面會自動進入404頁面 } } } else { if (whiteList.indexOf(to.path) !== -1) { // 在免登錄白名單,直接進入 next(); } else { next('/login'); // 否則全部重定向到登錄頁 } } })
4、側邊欄的可以從vuex裡面取數據來進行渲染
核心代碼是從router
取可以用的路由對象,來進行側邊欄的渲染,不管是前端動態加載還是後端動態加載路由,這個代碼都是一樣的
<!-- layout/components/siderbar.vue --> <el-menu :default-active="activeMenu" :collapse="isCollapse" :background-color="variables.menuBg" :text-color="variables.menuText" :unique-opened="false" :active-text-color="variables.menuActiveText" :collapse-transition="false" mode="vertical" > // 把取到的路由進行循環作為參數傳給子組件 <sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" /> </el-menu> // 獲取有權限的路由 routes() { return this.$router.options.routes } <!-- layout/components/siderbarItem.vue --> <template slot="title"> <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" /> </template> <sidebar-item v-for="child in item.children" :key="child.path" :is-nest="true" :item="child" :base-path="resolvePath(child.path)" class="nest-menu" /> props: { // route object item: { type: Object, required: true }, isNest: { type: Boolean, default: false }, basePath: { type: String, default: '' } }
前端控制路由,邏輯相對簡單,後端隻需要存這個用戶的角色就可以瞭,前端拿用戶的角色進行匹配。但是如果新增角色,就會非常痛苦,每一個都要加。
二、後端控制路由
後端控制大致思路是:路由配置放在數據庫表裡,用戶登錄成功後,根據角色權限,把有權限的菜單傳給前端,前端格式化成頁面路由識別的結構,再渲染到頁面菜單上;
- 用戶登錄以後,後端根據該用戶的角色,直接生成可訪問的路由數據,註意這個地方是數據
- 前端根據後端返回的路由數據,轉成自己需要的路由結構
具體邏輯:
- router.js裡面隻放一些靜態的路由,
login
、404之類 - 整理一份數據結構,存到表裡
- 從後端獲取路由數據,寫一個數據轉換的方法,講數據轉成可訪問的路由
- 也是維護一個
vuex
狀態,將轉換好的路由存到vuex
裡面 - 側邊欄也是從路由取數據進行渲染
因為前段控制和後端控制,後面的流程大部分都是一樣的,所以這個地方隻看看前面不一樣的流程:
1、store/permission.js,在vuex裡面發送請求獲取數據
GenerateRoutes({ commit }, data) { return new Promise((resolve, reject) => { getRoute(data).then(res => { // 將獲取到的數據進行一個轉換,然後存到vuex裡 const accessedRouters = arrayToMenu(res.data) accessedRouters.concat([{ path: '*', redirect: '/404', hidden: true }]) commit('SET_ROUTERS', accessedRouters) resolve() }).catch(error => { reject(error) }) }) }
2、整理一份數據結構,存到表裡
// 頁面路由格式 { path: '/form', component: Layout, children: [ { path: 'index', name: 'Form', component: () => import('@/views/form/index'), meta: { title: 'Form', icon: 'form' } } ] } // 整理後的數據格式 // 一級菜單 // parentId為0的就可以當做一級菜單,id最好是可以選4位數,至於為什麼等你開發項目的時候就知道瞭 { id: 1300 parentId: 0 title: "菜單管理" path: "/menu" hidden: false component: null hidden: false name: "menu" }, // 二級菜單 // parentId不為0的,就可以拿parentId跟一級菜單的id去匹配,匹配上的就push到children裡面 { id: 1307 parentId: 1300 title: "子菜單" hidden: false path: "menuItem" component: "menu/menuItem" // 要跟本地的文件地址匹配上 hidden: false name: "menuItem" }
3、寫一個轉化方法,把獲取到的數據轉換成router結構
export function arrayToMenu(array) { const nodes = [] // 獲取頂級節點 for (let i = 0; i < array.length; i++) { const row = array[i] // 這個exists方法就是判斷下有沒有子級 if (!exists(array, row.parentId)) { nodes.push({ path: row.path, // 路由地址 hidden: row.hidden, // 全部true就行,如果後端沒配 component: Layout, // 一般就是匹配你文件的component name: row.name, // 路由名稱 meta: { title: row.title, icon: row.name }, // title就是顯示的名字 id: row.id, // 路由的id redirect: 'noredirect' }) } } const toDo = Array.from(nodes) while (toDo.length) { const node = toDo.shift() // 獲取子節點 for (let i = 0; i < array.length; i++) { const row = array[i] // parentId等於哪個父級的id,就push到哪個 if (row.parentId === node.id) { const child = { path: row.path, name: row.name, hidden: row.hidden, // 核心代碼,因為二級路由的component是需要匹配頁面的 component: require('@/views/' + row.component + '/index.vue'), meta: { title: row.title, icon: row.name }, id: row.id } if (node.children) { node.children.push(child) } else { node.children = [child] } toDo.push(child) } } } return nodes } // 看下有沒有子級 function exists(rows, parentId) { for (let i = 0; i < rows.length; i++) { if (rows[i].id === parentId) return true } return false }
到此這篇關於vue實現動態路由詳細的文章就介紹到這瞭,更多相關vue實現動態路由內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- vue實現三級頁面跳轉功能
- 使用vue-element-admin框架從後端動態獲取菜單功能的實現
- vue2模擬vue-element-admin手寫角色權限的實現
- vue一步到位的實現動態路由
- 手把手教你vue實現動態路由