vue动态路由

前言

根据大师兄 - Vue 动态路由的实现(后台传递路由,前端拿到并生成侧边栏) 这篇文章了解到了目前对于前端的动态路由操作目前有两种方法:

第一种是花裤衩 - 手摸手,带你用 vue 撸后台 系列一(基础篇),因为本人没做过这块第一次做,看上去时候会觉得有点复杂,并且是对于角色进行管理,路由是由前端主要控制,并且会用到 vuex。

第二种就是看了大师兄 - Vue 动态路由的实现(后台传递路由,前端拿到并生成侧边栏)实现,也是本文主要方法,第一次尝试,这个方法也是需要后端同学比较配合,路由主要是由后端控制的。

第一步 后端准备

  • 先放上后端返回的 json 对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
[
{
"path": "",
"component": "layout/Layout",
"redirect": "noredirect",
"meta": {
"order": 1
},
"hidden": false,
"children": [
{
"path": "/",
"redirect": "/dashboard",
"meta": {
"order": 1
},
"hidden": true
},
{
"path": "/dashboard",
"name": "SY",
"component": "dashboard/index",
"meta": {
"title": "首页",
"icon": "example",
"order": 1
},
"hidden": false
}
]
},
{
"path": "/system",
"name": "SZ",
"component": "layout/Layout",
"redirect": "noredirect",
"meta": {
"title": "设置",
"icon": "example",
"order": 4
},
"hidden": false,
"children": [
{
"path": "/setting/adminSetting",
"name": "GLY",
"component": "setting/adminSetting",
"meta": {
"title": "管理员设置",
"icon": "form",
"order": 1
},
"hidden": false
},
{
"path": "/setting/sysSetting",
"name": "XT",
"component": "setting/sysSetting",
"meta": {
"title": "系统设置",
"icon": "form",
"order": 2
},
"hidden": false
}
]
},
{
"path": "/device",
"name": "SB",
"component": "layout/Layout",
"redirect": "noredirect",
"meta": {
"title": "设备",
"icon": "example",
"order": 3
},
"hidden": false,
"children": [
{
"path": "/device/deviceBind",
"name": "SBBD",
"component": "device/deviceBind",
"meta": {
"title": "设备绑定",
"icon": "form",
"order": 1
},
"hidden": false
},
{
"path": "/device/deviceList",
"name": "SBGL",
"component": "device/deviceList",
"meta": {
"title": "设备管理",
"icon": "form",
"order": 2
},
"hidden": false
},
{
"path": "/device/factory",
"name": "SBCS",
"component": "layout/SecondLayout",
"meta": {
"title": "设备厂商",
"icon": "form",
"order": 3
},
"hidden": false
}
]
},
{
"path": "/user",
"name": "KHGL",
"component": "layout/Layout",
"redirect": "noredirect",
"meta": {
"title": "客户管理",
"icon": "example",
"order": 2
},
"hidden": false,
"children": [
{
"path": "/user/permission",
"name": "QX",
"component": "user/permission",
"meta": {
"title": "权限",
"icon": "form",
"order": 3
},
"hidden": false
},
{
"path": "/user/userList",
"name": "KHLB",
"component": "user/userList",
"meta": {
"title": "客户列表",
"icon": "form",
"order": 1
},
"hidden": false
},
{
"path": "/user/label",
"name": "BQ",
"component": "user/label",
"meta": {
"title": "标签",
"icon": "form",
"order": 4
},
"hidden": false
},
{
"path": "/user/role",
"name": "JS",
"component": "user/role",
"meta": {
"title": "角色",
"icon": "form",
"order": 2
},
"hidden": false
}
]
}
]

以上是后端返回的 json 数据,这里别的其实都没什么问题,主要就是component这里,因为这里后端是返回不了 路由能用的,所以我们需要格式化

第二部 前端获取及格式化

这里的一个重要点其实是格式化,格式化路由能用的格式然后通过router.addRoutes()加载进去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
var getRouter // 初始化一个判断是否有路由的getRouter
router.beforeEach((to, from, next) => {
// ...
// 这里应该会有个判断,登录了才会获取菜单,因为菜单应该根据token获取,登录信息可以存在vuex里,然后判断是否有登录状态
if (!getRouter) {
// 不加这个判断,路由会陷入死循环
if (!getObjArr('router')) {
getUserMenu().then(res => {
saveObjArr('router', res.BusinessData) // 存储路由到localStorage
getRouter = getObjArr('router') // 从localStorage拿到了路由,这里没有好像是要刷新一次
routerGo(to, next) // 执行路由跳转方法
})
} else {
// 从localStorage拿到了路由
getRouter = getObjArr('router') // 拿到路由
routerGo(to, next)
}
}
}

function routerGo(to, next) {
// console.log('加载了')
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))
}

function filterAsyncRouter(asyncRouterMap) {
// 遍历后台传来的路由字符串,转换为组件对象
const accessedRouters = asyncRouterMap.filter(route => {
if (route.component) {
// layout/Layout这里是判断json里的父级Layout组件特殊处理
if (route.component === 'layout/Layout') {
route.component = Layout
// 这里看上面代码外层都是统一引用的这个,需要引入下见下面
} else {
route.component = _import(route.component)
// 其它组件引入_import代码见下面
}
}
if (route.children && route.children.length) {
route.children = filterAsyncRouter(route.children)
}
return true
})
return accessedRouters
}
  • Layout 这里是处理父级的,因为都是引用的相同位置
1
import Layout from '@/views/layout/Layout'
  • _import 这里是处理子组件引用的
1
2
3
4
5
6
7
8
const _import = require('@/utils/_import_' + process.env.NODE_ENV)
// 先引入,process.env.NODE_ENV是判断当前环境的,路径要改成自己的文件路径

// _import_development.js
module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+

// _import_production.js
module.exports = file => () => import('@/views/' + file + '.vue')

这样就把路由动态加载到了路由中啦~

附录 - element admin ui 排序

element admin框架中,左边菜单直接拿返回的路由 json 其实顺序会有问题,所以让后端在每个内容中加了个order进行排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<sidebar-item
v-for="route in routes"
:key="route.path"
:item="route"
:base-path="route.path"
/>
// 这是组件里的菜单加载,路径是src/views/layout/components/Sidebar/index.vue
routes() {
const oldMenu = JSON.parse(window.localStorage.getItem('router'))
// 旧的菜单
let newMenu = []
// 新的菜单
oldMenu.forEach(item => {
item.children = item.children.sort((a, b) => {
return a.meta.order - b.meta.order
}) // 这里是对每个一级以下的菜单遍历排序
})
newMenu = oldMenu.sort((a, b) => {
return a.meta.order - b.meta.order
}) // 这里是对每个一级菜单遍历排序
return newMenu
}