Вспомните реализацию маршрутизации динамического рендеринга Vue.

Vue.js

Предыстория: компания намеревается сделать (cms) первую ссылку на внешний интерфейс.vue-element-adminИдея проверяет возможность фронтальной аутентификации. Код, написанный боссом, ясен и достоин изучения. Позже, после рассмотрения многих факторов, сложности фронтальной аутентификации безопасности интерфейса и сложности проект, мы, наконец, решили использовать динамическую форму маршрутизации внутреннего рендеринга

Эта статья относительно устарела, см.Еще одна идея фоновой аутентификации во Vue — реализация и оптимизация динамической маршрутизации

Используется шаблон управления фоном Vue+Element.github

Идеи см. в статье

Реализация динамической маршрутизации Vue (прохождение маршрутизации в фоновом режиме, получение и создание боковой панели во внешнем интерфейсе)

issues/293

Реализовать идеи

Некоторые основные идеи и реализация динамической маршрутизации VueРеализация динамической маршрутизации Vue (прохождение маршрутизации в фоновом режиме, получение и создание боковой панели во внешнем интерфейсе)То же самое, основная часть добавила свое собственное понимание

  1. Каждый раз, когда маршрут переключается, сначала определите, следует ли выполнять вход в систему. После входа в систему будет выполняться логика, связанная с маршрутизацией.
  2. получить переменнуюgetRouter, если он существует, он будет освобожден напрямую, потому что существует конфигурация маршрутизации.
  3. Если вы обновите страницуgetRouterПеременная больше не существует, поэтому вам нужно получить ее один раз
  4. получают и хранят в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 }

обработанный маршрут

Обработанный нами маршрут нужно срастить с существующим роутером, а правила обработки маршрутов нужно модифицировать в соответствии с требованиями.

[{
  "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的全局变量
    },
   	// ....
  }

Таким образом реализуется роутинг, передаваемый бэкендом динамического рендеринга. Я чувствую, что он все еще используется там, где его можно оптимизировать. Добро пожаловать, чтобы исправить меня.