Vue 动态路由的实现(后台传递路由,前端拿到并生成侧边栏)
2019.4.28号更新:
鉴于大家有很多疑问,特地创建了群1020135830,有问题的多交流交流;
2019.4.3号更新:
我说为啥点进来看不到最新的评论,今天才发现是被折叠了,
这篇教程大家理解起来如果有难度的话,
参考下把路由表写到本地,后端返回用户角色去做匹配这种方式,适合上手:本地写好路由,向后端请求用户的角色
以下为原答案:
地狱空荡荡,师兄在土创。虽然土,但是可以疗伤。
前言
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部分 ,这里我就不贴代码了
才疏学浅,希望大家多多指正,尤其对于路由拦截那部分,应该还有很多优化的地方,欢迎指正
推荐阅读
mpvue写小程序遇到的问题总结(自己用mpvue实现一个小程序的tabbar,vantUi的使用)
公司要开发一个小程序,面向用户分为3种角色,所以要自己手写个tabbar(文章编辑中,届时将链接放过来)因为也是时隔很久写小程序,就选择了跟vue相近的mpvue来写,或多或少也遇到了点问题,现总结一下。
大师兄赞 5阅读 5.1k评论 4
Vue中的diff算法
diff算法是一种通过同层的树节点进行比较的高效算法,避免了对树进行逐层搜索遍历,所以时间复杂度只有 O(n)。diff算法的在很多场景下都有应用,例如在 vue 虚拟 dom 渲染成真实 dom 的新旧 VNode 节点比较更新时...
款冬赞 27阅读 13.4k评论 7
给我实现一个前端的 Excel 导入和导出功能
前言【负责人 A】:现在报表部分基于接口的 Excel 的导入和导出功能有点慢,前端这边能不能实现一下这个功能,然后我们在比对看看效果!【切图仔 B】: 接口这边不能优化一下吗?比如排查下慢的原因什么的。【负...
熊的猫赞 19阅读 2.6k
vue-property-decorator使用手册
@Component 装饰器可以接收一个对象作为参数,可以在对象中声明 components ,filters,directives等未提供装饰器的选项,也可以声明computed,watch等
似曾相识赞 17阅读 29.1k评论 7
一个开源vue网站博客,nuxt开源网站,前后端分离项目
开媛笔记,基于nuxt ssr首屏服务器端渲染 。用于分享、记录、交流和学习,希望可以帮助到小伙伴们。同时网站在不断更新,创造属于猿(媛)的世界 -$Bao Yalong ..Let's Go! [链接]
jigsaw赞 16阅读 8.4k评论 3
你知道前端水印功能是怎么实现的吗?
前一段时间由于项目需要实现水印功能,于是去了解了相关的内容后,基于 Vue 的实现了一个 v-watermark 指令完成了对应的功能,其实整体内容并不复杂!
熊的猫赞 14阅读 1.7k
2022 你还不会微前端吗 (上) — 从巨石应用到微应用
微前端系列分为 上/下 两篇,本文为 上篇 主要还是了解微前端的由来、概念、作用等,以及基于已有的微前端框架进行实践,并了解微前端的核心功能所在,而在下篇 2022 你还不会微前端吗 (下) — 揭秘微前端核心原理...
熊的猫赞 14阅读 1.6k
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。