131

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部分 ,这里我就不贴代码了

才疏学浅,希望大家多多指正,尤其对于路由拦截那部分,应该还有很多优化的地方,欢迎指正

你可能感兴趣的

146 条评论
Kimstroy · 2018年06月29日

你遍历router进行渲染就可以了

回复

1

遍历router有很多种方法,主要是要从后台取到路由,并处理成前端可用的格式,并且要把拿到的路由,add进路由里才能正常使用

大师兄 作者 · 2018年06月29日
胡里奥 · 2018年06月29日

感谢大神特意为了我的提问写了篇教程...

回复

0

非大神,土创😆

大师兄 作者 · 2018年06月29日
luoshushu · 2018年07月07日

npm install
报错.... 咋办?我想试试效果

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! chromedriver@2.40.0 install: node install.js
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the chromedriver@2.40.0 install script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

回复

0

https://blog.csdn.net/u012527...;不太确定可以不可以,你百度错误下试试

大师兄 作者 · 2018年07月09日
0

大佬 大佬 我司是从后台那拿到路由表,我前端这么进行渲染, 用addRoutes添加的时候 刷新了 就跳到404 页了 咋办?? 能不能给点思路???

luoshushu · 2018年07月09日
0

@luoshushu 看看这个--->Vue 动态路由的实现(后台传递路由,前端拿到并生成侧边栏)https://segmentfault.com/a/11...

大师兄 作者 · 2018年07月09日
huangwenda · 2018年07月29日

写的真好 很详细 适合我这种菜鸟。 非常受益 跟我的项目相似度很高。 不知道作者能不能及时看到 想问一下问题

回复

0

你遇到什么问题了吗

大师兄 作者 · 2018年07月29日
0

感动到哭 我的项目进去好多报错啊

huangwenda · 2018年07月29日
胡里奥 · 2018年08月07日

大佬在吗 救命

回复

0

召唤

胡里奥 · 2018年08月07日
0
胡里奥 · 2018年08月07日
0

@胡里奥 直接说问题

大师兄 作者 · 2018年08月07日
转转11 · 2018年08月15日

大师兄,从路由生成左侧栏,左侧栏里的图片怎么加进去呢。我微信lcb1132,我加你或者你加我都行,拜托了

回复

0

children: [{

        path: 'dashboard',
        component: _import('dashboard/index'),
        name: 'dashboard',
        meta: { title: '测试区', icon: 'addition', noCache: true }
    }]

meta标签里有个icon标签,把你的icon名称放到这就行了

大师兄 作者 · 2018年08月15日
0

icon怎么绑定,同问

Learn · 2018年10月16日
郑家好人 · 2018年08月22日

太赞了!

回复

鱼鱼鱼鱼 · 2018年08月23日

为什么beforeeach不好使

回复

鱼鱼鱼鱼 · 2018年08月23日

渲染出错怎么办啊 Cannot find module './/brand/home/index.vue'. 救命啊大佬

回复

0

额鹅鹅鹅,刚翻到这个问题,你应该解决了吧【尴尬】【尴尬】

大师兄 作者 · 2018年11月15日
0

请问这个问题解决了吗,我打包的时候也遇到了

方块 · 2018年11月22日
0

同问 有解决的方法吗

DuoDuo · 2018年12月05日
Leon · 2018年10月17日

process.env.NODE_ENV 是什么东西,对应着哪个文件? require('./router/_import_' + process.env.NODE_ENV)这里是用来区别开发模式和生产模式的吗?

回复

0

对的,process.env.NODE_ENV = 'production'在build/build.js,你下边的疑问说的也是对的

大师兄 作者 · 2018年10月18日
Leon · 2018年10月18日

大体步骤:拦截路由->后台取到路由->保存路由到localStorage(用户登录进来只会从后台取一次,其余都从本地取,所以用户,只有退出在登录路由才会更新),意思是在登录页面的时候自己手动去清除吗?

回复

jingke · 2018年10月18日

路由懒加载好像不能用

回复

0

/src/router下的,_import_development.js/_import_production.js就是做懒加载用的

大师兄 作者 · 2018年10月18日
uuuugg · 2018年10月29日

没有登陆,不知道角色,就去获取路由表吗

回复

0

我这只写大概的实现方式,具体的业务场景要具体分析。一般来讲,要先登录上才去获取对应的路由表

大师兄 作者 · 2018年10月29日
行走于细雨中的 · 2018年10月30日

子路由component: () => import("@/views/model" ), 正常显示 但是遍历数组之后用component: () => import("@/views" + arr[i].children[j].url),这种方法,一直报错TypeError: Cannot read property 'children' of undefined, 这是为什么呢

回复

0

您这个解决啦吗 我也遇到这个问题

MAIST · 7月17日
0

看见别人的就没有问题,自己写的就是报错,咋回事么

gethin · 7月30日
下雨那天 · 2018年10月31日

很有用~我实在each中使用router.addrouters,如果不吧已经有的router存储在外面的话会出现死循环,好像beforeEach是异步的

回复

mylxx · 2018年11月14日

大师兄 我按你的这个来写的 列表里的查看 编辑啥的 路由应该怎么弄?

回复

0

自己本地配个固定路由,跟后台获取到的路由进行结合,你想怎么跳转都可以

大师兄 作者 · 2018年11月15日
0

嗯嗯 刷新后左侧菜单对应项不会自动展开

mylxx · 2018年11月15日
0

@mylxx 你网址中的链接要跟你渲染菜单时 ,标签: <el-menu-item :index="itemChild.href">{{itemChild.title}}</el-menu-item> 中的index属性对应上,具体的可参看elementui的官网,关于菜单的部分

大师兄 作者 · 2018年11月16日
放纵放纵梦想 · 2018年11月23日

大师兄,我启动后只有一个cannot get/ 怎么回事啊,怎么分析流程啊

回复

子川先生 · 2018年12月03日

大师兄,TypeError:asyncRouterMap.filter is not a function怎么处理

回复

0

打印一下 asyncRouterMap是个什么,估计没找到参数,你看下调用filterAsyncRouter的地方,看下参数有没有传正确

大师兄 作者 · 2018年12月05日
0

请问你解决了这个问题吗?我也遇到了

朱小龙 · 4月4日
大师兄 作者 · 2018年12月04日

打印一下 asyncRouterMap是个什么,估计没找到参数,你看下调用filterAsyncRouter的地方,看下参数有没有传正确

回复

DuoDuo · 2018年12月05日

大师兄 npm run dev 后渲染报错: Cannot find module '@/xx/xxx/xxx.vue' 路径是对的 这个怎么破啊

回复

0

???没有啊,我从git上拉了一份下来,没问题啊

大师兄 作者 · 2018年12月05日
0

参考着你的 把他用到自己的项目中了,字符串转成组件的时候除了 _import_development.js这两个文件中的内容 还有什么要配置的吗??

DuoDuo · 2018年12月05日
0

@DuoDuo 看下_import_development.js里的配置,你的页面要放在views目录下,当然你可以自己修改,module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+
,看下是不是这个导致的

大师兄 作者 · 2018年12月05日
载入中...