摘錄:鑒於大家有很多疑問,特地創建了群1020135830,有問題的多交流交流;
我說為啥點進來看不到最新的評論,今天才發現是被折疊了,
這篇教程大家理解起來如果有難度的話,
參考下把路由表寫到本地,后端返回用戶角色去做匹配這種方式,適合上手:本地寫好路由,向后端請求用戶的角色
以下為原答案:
地獄空盪盪,師兄在土創。雖然土,但是可以療傷。
前言
vue項目實現動態路由的方式大體可分為兩種:
1.前端這邊把路由寫好,登錄的時候根據用戶的角色權限來動態展示路由,(前端控制路由)
詳情可參閱花褲衩大佬的項目 手把手...,我當時看這個項目看了好久才明白一點邏輯,
因為大神的動態路由那里有好多層判斷,並且穿插各種vuex,把小白的我都快搞懵逼了,對我啟發很大,也正是這篇文章,給我提供了很多邏輯
2.后台傳來當前用戶對應權限的路由表,前端通過調接口拿到后處理(后端處理路由)
這兩種方法各有優點,效果都能實現,我們公司是通過第二中種方法實現的,原因就是公司項目里有一個專門的用戶中心,里邊邏輯很復雜,不好返給前端用戶權限,擔心路由放到前端
不安全(以上的話是公司的后台同學講的),那好吧,抱着都試試、鍛煉下自己能力的態度,
我們搞了第二種方法。
今天我們來講講用后台傳遞路由表實現動態路由的思路,因為公司的項目里路有部分用到了vuex,我就把路由部分脫離vuex整理了出來,讓大家有個啟發,並不是絕對的解決方案,只是思路
github:https://github.com/Mrblackant...
在線查看:http://an888.net/antRouter/#/...
思路整理
以下四步驟對應的代碼都在下方會講到,並且是對應的
1.后台同學返回一個json格式的路由表,我用easymock造了一段: 動態路由表,大家可參考;
2.因為后端同學傳回來的都是字符串格式的,但是前端這里需要的是一個組件對象啊,寫個方法遍歷一下,將字符串轉換為組件對象;
3.利用vue-router的beforeEach、addRoutes、localStorage來配合上邊兩步實現效果;
4.左側菜單欄根據拿到轉換好的路由列表進行展示;
大體步驟:攔截路由->后台取到路由->保存路由到localStorage(用戶登錄進來只會從后台取一次,其余都從本地取,所以用戶,只有退出在登錄路由才會更新)
代碼
1.路由表
每個路由都使用到組件Layout,這個組件是整體的頁面布局:左側菜單列,右側頁面,所以children下邊的第一級路由就是你自己的開發的頁面,meta里包含着路由的名字,以及路由對應的icon;
因為可能會有多級菜單,所以會出現children下邊嵌套children的情況;
路由是數組格式
"data": { "router": [ { "path": "", "component": "Layout", "redirect": "dashboard", "children": [ { "path": "dashboard", "component": "dashboard/index", "meta": { "title": "首頁", "icon": "dashboard" } } ] }, { "path": "/example", "component": "Layout", "redirect": "/example/table", "name": "Example", "meta": { "title": "案例", "icon": "example" }, "children": [ { "path": "table", "name": "Table", "component": "table/index", "meta": { "title": "表格", "icon": "table" } }, { "path": "tree", "name": "Tree", "component": "tree/index", "meta": { "title": "樹形菜單", "icon": "tree" } } ] }, { "path": "/form", "component": "Layout", "children": [ { "path": "index", "name": "Form", "component": "form/index", "meta": { "title": "表單", "icon": "form" } } ] }, { "path": "*", "redirect": "/404", "hidden": true } ] }
2.將后端傳回的"component": "Layout", 轉為"component": Layout組件對象
因為有多級路由的出現,所以要寫成遍歷遞歸方法,確保把每個component轉成對象,
因為后台傳回的是字符串,所以要把加載組件的過程 封裝成一個方法(此處參考花褲衩大神的解決方法),用這個方法在遍歷中使用;詳情查看項目里的router文件夾下的 _import_development.js和_import_production.js文件
Layout我放的目錄跟其他文件的目錄不一樣,所以我在遍歷里單獨處理,各位小伙伴可自己調整哈
const _import = require('./router/_import_' + process.env.NODE_ENV)//獲取組件的方法 import Layout from '@/views/layout' //Layout 是架構組件,不在后台返回,在文件里單獨引入 function filterAsyncRouter(asyncRouterMap) { //遍歷后台傳來的路由字符串,轉換為組件對象 const accessedRouters = asyncRouterMap.filter(route => { if (route.component) { **加粗文字** if (route.component === 'Layout') {//Layout組件特殊處理 route.component = Layout } else { route.component = _import(route.component) } } if (route.children && route.children.length) { route.children = filterAsyncRouter(route.children) } return true }) return accessedRouters }
3.使用beforeEach、addRoutes、localStorage來配合實現
beforeEach路由攔截,進入判斷,如果發現本地沒有路由數據,那就利用axios后台取一次,取完以后,利用localStorage存儲起來,利用addRoutes動態添加路由,
ps:beforeEach好壞啊,一步小心就進入到了他的死循環,瀏覽器都tm崩了,得在一開始就加判斷,拿到路由了,就直接next(),嚶嚶嚶
global.antRouter是為了傳遞數據給左側菜單組件進行渲染
import axios from 'axios' var getRouter //用來獲取后台拿到的路由 router.beforeEach((to, from, next) => { if (!getRouter) {//不加這個判斷,路由會陷入死循環 if (!getObjArr('router')) { axios.get('https://www.easy-mock.com/mock/5a5da330d9b48c260cb42ca8/example/antrouter').then(res => { getRouter = res.data.data.router//后台拿到路由 saveObjArr('router', getRouter) //存儲路由到localStorage routerGo(to, next)//執行路由跳轉方法 }) } else {//從localStorage拿到了路由 getRouter = getObjArr('router')//拿到路由 routerGo(to, next) } } else { next() } }) function routerGo(to, next) { getRouter = filterAsyncRouter(getRouter) //過濾路由 router.addRoutes(getRouter) //動態添加路由 global.antRouter = getRouter //將路由數據傳遞給全局變量,做側邊欄菜單渲染工作 next({ ...to, replace: true }) } function saveObjArr(name, data) { //localStorage 存儲數組對象的方法 localStorage.setItem(name, JSON.stringify(data)) } function getObjArr(name) { //localStorage 獲取數組對象的方法 return JSON.parse(window.localStorage.getItem(name)); }
4.拿到遍歷好的路由,進行左側菜單渲染
上邊第三部會給 global.antRouter賦值,這是一個全局變量(可以用vuex替代),菜單那邊拿到路由,進行渲染,這里又是參考了花褲衩大神的layout部分 ,這里我就不貼代碼了
才疏學淺,希望大家多多指正,尤其對於路由攔截那部分,應該還有很多優化的地方,歡迎指正