记一次Vue动态渲染路由的实现

栏目: 编程语言 · 发布时间: 6年前

内容简介:背景: 公司打算做一个(cms),最开始前端参照使用的是Vue+Element的后台管理模板思路参考了一下文章

背景: 公司打算做一个(cms),最开始前端参照 vue-element-admin 的思路,验证了前端鉴权的可能性,大佬写的代码思路清奇,值得学习,后来考虑诸多因素,接口安全性 前端鉴权的难度 以及项目的复杂度,最后选择采用后端渲染动态路由的形式

使用的是Vue+Element的后台管理模板 github

思路参考了一下文章

Vue 动态路由的实现(后台传递路由,前端拿到并生成侧边栏)

issues/293

实现思路

基础的一些思路和Vue 动态路由的实现 Vue 动态路由的实现(后台传递路由,前端拿到并生成侧边栏) 一样,核心部分加入了自己的理解

记一次Vue动态渲染路由的实现
getRouter
getRouter
getRouter

以下为具体实现思路

配置基础路由

基础路由为不登录也可以访问的路由

const StaricRouterMap = [
  {
    path: '/login',
    component: login,
    hidden: true
  },
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    name: 'dashboard',
    hidden: true,
    meta: { title: '根目录' },
    children: [
      {
        path: 'dashboard',
        component: () => import('@/views/dashboard/index')
      }
    ]
  },
  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true
  },
  {
    path: '*',
    redirect: '/404',
    hidden: true
  }, // .... 
]
export default new Router({
  mode: 'history', // 后端支持可开
  scrollBehavior: () => ({ y: 0 }),
  routes: StaricRouterMap
})
复制代码

与后端同学定制路由结构 (以下为json)

后端会根据当前用户权限动态返回路由结构 前端不再需要考虑权限问题

[{
  "id": 1,
  "name": "Nested",
  "code": null,
  "description": null,
  "url": "/nested",
  "generatemenu": 0,
  "sort": 0,
  "parentId": null,
  "permName": null,
  "redirect": "/nested/menu1",
  "title": "Nested",
  "icon": "nested",
  "children": [{
    "id": 2,
    "name": "Menu1",
    "code": null,
    "description": null,
    "url": "menu1",
    "generatemenu": 0,
    "sort": 0,
    "parentId": 1,
    "permName": null,
    "redirect": "",
    "title": "Menu1",
    "icon": "menu1",
    "children": [{
      "id": 4,
      "name": "Menu1-1",
      "code": null,
      "description": null,
      "url": "menu1-1",
      "generatemenu": 0,
      "sort": 0,
      "parentId": 2,
      "permName": null,
      "redirect": "",
      "title": "Menu1-1",
      "icon": "",
      "children": null
    }, {
      "id": 5,
      "name": "Menu1-2",
      "code": null,
      "description": null,
      "url": "menu1-2",
      "generatemenu": 0,
      "sort": 0,
      "parentId": 2,
      "permName": null,
      "redirect": "",
      "title": "Menu1-2",
      "icon": "",
      "children": null
    }]
  }, {
    "id": 3,
    "name": "Menu2",
    "code": null,
    "description": null,
    "url": "menu2",
    "generatemenu": 0,
    "sort": 0,
    "parentId": 1,
    "permName": null,
    "redirect": "",
    "title": "Menu2",
    "icon": "menu2",
    "children": null
  }]
}]
复制代码

解析后端初始路由数据为可用数据

当然这不是直接用于渲染路由 我们需要进行递归处理成为我们想要的数据

../router/_import

export default file => {
  return map[file] || null
}

const map = {
  Nested: () => import('@/views/layout/Layout'),
  Menu1: () => import('@/views/nested/menu1/index'),
  'Menu1-1': () => import('@/views/nested/menu1/menu1-1'),
  'Menu1-2': () => import('@/views/nested/menu1/menu1-2')
}
复制代码

处理后端原始路由数据

../utils/addRouter

import _import from '../router/_import'// 获取组件的方法

function addRouter(routerlist) {
  routerlist.forEach(e => {
    // 删除无用属性
    delete e.code
    delete e.sort
    delete e.generatemenu
    delete e.description
    delete e.permName
    delete e.id
    delete e.parentId

    e.path = e.url
    delete e.url
    e.component = _import(e.name) // 动态匹配组件
    if (e.redirect === '') {
      delete e.redirect
    }
    if (e.icon !== '' && e.title !== '') { // 配置 菜单标题 与 图标
      e.meta = {
        title: e.title,
        icon: e.icon
      }
    }
    if (e.title !== '' && e.icon === '') {
      e.meta = {
        title: e.title
      }
    }
    delete e.icon
    delete e.title
    if (e.children != null) {
      // 存在子路由就递归
      addRouter(e.children)
    }
  })
  return routerlist
}

export { addRouter }
复制代码

处理后的路由

我们处理后的路由后面需要与现有的router进行拼接,这里需要根据需求 修改处理路由的规则

[{
  "name": "Nested",
  "redirect": "/nested/menu1",
  "children": [{
    "name": "Menu1",
    "children": [{
      "name": "Menu1-1",
      "children": null,
      "path": "menu1-1",
      "meta": {
        "title": "Menu1-1"
      }
    }, {
      "name": "Menu1-2",
      "children": null,
      "path": "menu1-2",
      "meta": {
        "title": "Menu1-2"
      }
    }],
    "path": "menu1",
    "meta": {
      "title": "Menu1",
      "icon": "menu1"
    }
  }, {
    "name": "Menu2",
    "children": null,
    "path": "menu2",
    "component": null,
    "meta": {
      "title": "Menu2",
      "icon": "menu2"
    }
  }],
  "path": "/nested",
  "meta": {
    "title": "Nested",
    "icon": "nested"
  }
}]
复制代码

(核心)合并路由

以上的都是准备工作,就是为了将 初始路由 与后端返回的 动态路由 进行拼接

这里也是最复杂的一块,参考了一些别人的思路,后来完成了这个版本,这就是最上面 实现思路 的代码

import router from './router'
import store from './store'
import NProgress from 'nprogress' // Progress 进度条
import 'nprogress/nprogress.css' // Progress 进度条样式
import { Message } from 'element-ui'
import { addRouter } from './utils/addRouter'

var getRouter
router.beforeEach((to, from, next) => {
  NProgress.start() // 进度条
  if (localStorage.getItem('login_static') === '1') {
    if (to.path === '/login') {
      Message('您已登录,如需切换用户请退出重新登录')
      next({ path: '/' })
    }
    if (!getRouter) {
      if (getRouterList('router')) {
        // 路由信息存在 说明请求阶段以及完成 直接解析路由信息
        getRouter = getRouterList('router') // 拿到路由
        gotoRouter(to, next)
      } else {
        // localStorage不存在路由信息 这需要 请求路由信息 并解析路由
        setRouterList(to, next) // 存储路由到localStorage
      }
    } else {
      // getRouter变量存在 说明路由信息存在 直接通过
      next()
    }
  } else {
    if (to.path === '/login') {
      next()
    } else {
      next(`/login`)
    }
  }
})

router.afterEach(() => {
  NProgress.done() // 结束Progress
})

function gotoRouter(to, next) {
  try {
    getRouter = addRouter(getRouter)
    router.addRoutes(getRouter) // 动态添加路由
    global.antRouter = router.options.routes.concat(getRouter) // 将路由数据传递给全局变量,做侧边栏菜单渲染工作
    next({ to, replace: true })
  } catch (error) {
    localStorage.setItem('login_static', 0)
  }
}

function setRouterList(to, next) {
  store.dispatch('getRouter').then(asyncRouter => { // 请求路由数据
    localStorage.setItem('router', JSON.stringify(asyncRouter))
    getRouter = getRouterList('router') // 拿到路由
    gotoRouter(to, next)
  })
}

function getRouterList(name) {
  return JSON.parse(localStorage.getItem(name))
}
复制代码

修改侧边栏的应用路由地址

需要注意的是 通过 addRoutes合并的路由 不会被 this.$router.options.routes 获取到,所以需要将获取的路由拼接到 this.$router.options.routes

最后修改渲染侧边栏部分部分的代码

src\views\layout\components\Sidebar\index.vue

computed: {
	// ....
    routes() {
      return global.antRouter // 这里应该最好使用vuex的全局变量
    },
   	// ....
  }
复制代码
记一次Vue动态渲染路由的实现

这样就实现了动态渲染后端传递的路由,,感觉还是用可以优化的地方,欢迎指正


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Uberland

Uberland

Alex Rosenblat / University of California Press / 2018-11-19 / GBP 21.00

Silicon Valley technology is transforming the way we work, and Uber is leading the charge. An American startup that promised to deliver entrepreneurship for the masses through its technology, Uber ins......一起来看看 《Uberland》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

随机密码生成器
随机密码生成器

多种字符组合密码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换