vue动态菜单、动态路由加载以及刷新踩坑实战

目录
  • 需求:
  • 思路:
  • 教训:
  • 分享正文:
  • 总结

需求:

从接口动态获取子菜单数据 动态加载 要求只有展开才加载子菜单数据 支持刷新,页面显示正常

思路:

一开始比较乱,思路很多。想了很多

首先路由和菜单共用一个全局route, 数据的传递也是通过store的route, 然后要考虑的俩个点就是一个就是渲染菜单和加载路由,可以在导航首位里处理路由,处理刷新。

还有一个地方就是菜单组件里展开事件里面 重新生成菜单数据,路由。大体思路差不多,做完就忘了..... 刷新的问题需要用本地缓存处理,之前一直缓存这个route 大数据,但是这个localstore 缓存的只是字符串,不能缓存对象,这样的话,菜单是出来了,动态的路由404,因为json.parse 转出来的对象 不是真实路由数据,还需要单独处理component 这个是个函数对象,
都是坑....所以之前走了点弯路,思路没想好。

第二天,重新整理思路,想了下,为啥要缓存整个route对象,傻是不是,动态的数据只是一部分,三级菜单...何不分开存储,本地存储动态菜单数据,利用完整的路由模板,取出来的初始化路由对象,然后,循环菜单数据,动态设置children属性,生成一个新的完整的路由对象,addRoute不是更好吗
想到这里,整理下完整思路

【定义全局route对象】=> 【导航首位判断刷新、初始化加载 store中route为空】=> 【初始化路由和菜单】=> 【菜单展开事件里面,请求接口,拿到子菜单数据,localStore 存储菜单数据,更新路由】
还有一些小坑 比如重复路由、刷新404问题、刷新白屏、异步处理...

教训:

问题肯定能解决,折腾几天,最后才发现思路最重要

思路错误,就是浪费时间

先想好思路,完整的实现路线 先干什么后干什么 其中遇到技术难点再去百度

分享正文:

暴力贴代码!!!!!!!!!!!!!

全局定义store route对象 都会,忽略

import Vue from 'vue'
import Router from 'vue-router'
import Layout from '@/layout'

Vue.use(Router)

export const constantRoutes = [{
  path: '/login',
  name: 'login',
  component: () => import('@/views/login/index'),
  hidden: true,
}, {
  path: '/404',
  name: '404',
  component: () => import('@/views/error-page/404'),
  hidden: true
}, {
  path: '/401',
  name: '401',
  component: () => import('@/views/error-page/401'),
  hidden: true
}, {
  path: '/',
  component: Layout,
  redirect: '/dashboard',
  children: [
    {
      path: 'dashboard',
      component: () => import('@/views/dashboard/index'),
      name: 'dashboard',
      meta: { title: '首页', icon: 'documentation' }
    },
    {
      path: 'xxx',
      component: () => import('xxxxx'),
      name: 'xxx',
      meta: { title: 'XXX', icon: 'component' },
      children: [
          {
            path: 'host',
            name: 'host',
            meta: {
              title: 'xxx',
              key: 'host'
            }
          },
          {
            path: 'control',
            name: 'control',
            alwaysShow: true,
            meta: {
              title: 'xxx',
              key: 'control'
            },
            children: []
          },
          {
            path: 'signal',
            name: 'signal',
            alwaysShow: true,
            meta: {
              title: 'xxx',
              key: 'signal',
            },
            children: []
          },
          {
            path: 'gateway',
            name: 'gateway',
            alwaysShow: true,
            meta: {
              title: 'xxx',
              key: 'gateway'
            },
            children: []
          }
      ]
    },
    {
      path: 'meeting',
      name: 'meting',
      meta: { title: 'xxx', icon: 'list' }
    },
    {
      path: 'traces',
      component: () => import('@/views/xxx'),
      name: 'traces',
      meta: { title: 'xxx', icon: 'chart' }
    }
]
},
  {
    path: '*',
    redirect: '/404',
    hidden: true
  }
]

const router = new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({
    y: 0
  }),
  //routes: constantRoutes 守卫初始化,这里注释掉
})

//路由重复的问题 解决
router.$addRoutes = (params) => {
  router.matcher = new Router({ // 重置路由规则
    scrollBehavior: () => ({
      y: 0
    })
  }).matcher
  router.addRoutes(params) // 添加路由
}

export default router
//监听路由守卫 生成动态路由
router.beforeEach((to, from, next) => {

  const routes = store.state.app.routes

  console.error('beforeEach 守卫执行了')

  //处理首次加载 刷新
  if(routes.length === 0){
     console.error('首次/刷新了')

     //更新路由缓存
     const cacheRoute = getLocalRouteInfo()

     const routeValue = asyncRouteDataToRoute(cacheRoute.asyncRouteData, constantRoutes)

     store
      .dispatch('app/setRoutes', routeValue)

     router.$addRoutes([...routeValue])

     next({
        ...to,
        replace: true
     })
     return
   } 

   next()
})
/**
 * 更新三级子菜单 路由元数据
 */
export const updateIPChildRoutes = function(routes, path, children) {
    return setRouteArrayChildren(routes, path, children)
}

/**
 * 根据父菜单加载子菜单
 * @param {*} routeKey
 * @returns
 */
export const generateIPChildRoutes =  function(routeKey) {
    return new Promise((resolve, reject) => {
      if (!routeKey) return

      // const start = getDateSeconds(new Date())
      // const end = setDateSeconds(new Date(), 15, 'm')

      const filterAddr = grafanaAddrs.filter(addr => addr.key === routeKey)[0]
      const matchup = filterAddr.matchup

      const params = {
        matchup
      } 

      //动态添加routers
      try {
        fetchIPInstance(params).then(ipAddrs => {
          const ipRoutes = []
          ipAddrs.forEach(
            addr => {
                const ipInstance = addr.instance.replace(/^(.*):.*$/, "$1")

                if(!isIPAddress(ipInstance))
                  return

                const existRoute = ipRoutes.find(ip => ip.meta && ip.meta.key === ipInstance)

                !existRoute && ipRoutes.push(
                  {
                    path: ipInstance,
                    name: ipInstance,
                    meta: {
                        title: ipInstance,
                        key: ipInstance
                    }
                  }
                )
            }
          )
          resolve(ipRoutes)
        })
      } catch (error) {
        reject(error)
        console.error(`加载子菜单错误`)
      }
    })
}
import { isArray, setRouteArrayChildren } from './tool'

// 设置路由缓存值
const localRouteKey = "LOCALROUTESET";

/**
 * currentPath: '' //当前访问的路由路径
 * routeData: [], //存储的完整路由数据(仅加载菜单可用)
 * asyncRouteData: [] //动态的路由数据(生成新路由使用)
 * {
 *    parentKey //父级key
 *    route: [
 *      {
            path: ,
            name: ,
            meta: {
                title: ,
                key:
            }
        }
 *    ]
 * }
 */

export function getLocalRouteInfo() {
  const data = localStorage.getItem(localRouteKey);

  return data ? JSON.parse(data) : {};
}

export function setLocalRouteInfo(data) {
  const localData = getLocalRouteInfo();

  localStorage.setItem(
    localRouteKey,
    JSON.stringify({
      ...localData,
      ...data,
    })
  );
}

export function removeLocalRouteInfo() {
  localStorage.removeItem(localRouteKey);
}

/**
 * 本地缓存 转化成路由元数据
 * @param {*} constantRoutes 路由模板
 */
export function asyncRouteDataToRoute(asyncRouteData, constantRoutes) {
   let route = constantRoutes
   if (isArray(asyncRouteData) && asyncRouteData.length > 0) {
     asyncRouteData.forEach(
        data => {
          route = setRouteArrayChildren(route, data.parentKey, data.route)
        }
     )
   }
   return route
}
/**
 * 设置路由children属性
 * @param {*} routes
 * @param {*} path
 * @param {*} children
 * @returns
 */
export const setRouteArrayChildren = function(routes, path, children) {

  if (!isArray(routes) || !path)
     return new Array()

  for (const route of routes) {
    if (isArray(route.children)) {
      if (route.path === path && route.children.length === 0) {
        route.children.push(...children)
      } else {
        setRouteArrayChildren(route.children, path, children)
      }
    }
  }

  return routes
}
onExpandMenu(key, keyPath) {
      console.error(key, keyPath)

      const path = key.substring(key.lastIndexOf('/') + 1)
      console.error(path)

      //动态生成监控三级菜单/路由
      const ipAddrKeys = []
      grafanaAddrs.forEach(
        addr => {
          if (addr.matchup) {
            ipAddrKeys.push(addr.key)
          }
        }
      )

      if (path && ipAddrKeys.includes(path)) {

         generateIPChildRoutes(path)
         .then(ipAddrs => {

          if (isArray(ipAddrs)) {

            //缓存动态路由数据
            const localRouteInfo = getLocalRouteInfo()
            const cacheRoutes = localRouteInfo.asyncRouteData || []
            cacheRoutes.push(
                {
                    parentKey: path,
                    route: ipAddrs
                }
              )
            setLocalRouteInfo({
              asyncRouteData : cacheRoutes
            })

            //更新route
            let asyncRoutes = store.state.app.routes

            asyncRoutes = updateIPChildRoutes(asyncRoutes, path, ipAddrs)

            store
              .dispatch('app/setRoutes', asyncRoutes)

            router.$addRoutes([...asyncRoutes])

          }
         })
      }
    }

其他代码 不是核心的 不贴了

总结

到此这篇关于vue动态菜单、动态路由加载以及刷新踩坑的文章就介绍到这了,更多相关vue动态菜单、动态路由加载内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • vue动态路由配置及路由传参的方式

    动态路由: 当我们很多个页面或者组件都要被很多次重复利用的时候,我们的路由都指向同一个组件,这时候从不同组件进入一个"共用"的组件,并且还要传参数,渲染不同的数据 这就要用到动态路由跟路由传参了! 首先我们来了解下router-link这个组件: 简单来说,它是个导航器,利用to属性导航到目标组件,并且在渲染的时候会自动生成一个a标签,当然官方也有说明,加个tag标签属性就可以渲染不同的标签,可以浏览器端查看到 并且当一个导航器被激活的时候,会自动加上一个css的激活样式,可以全局在路

  • 讲解vue-router之什么是动态路由

    前言: 今天我来给大家说道说道v-router,这是个什么东西?我们先从动态路由讲起. GitHub:https://github.com/Ewall1106/mall/tree/master 1.动态路由有一个什么适用场景呢? 比如在写商品详情页面的时候,页面结构都一样,只是商品id的不同,所以这个时候就可以用动态路由动态. 2.官方文档 首先我们来看看官方文档上是怎么解释动态路由的?(https://router.vuejs.org/zh-cn/) 你可以在一个路由中设置多段"路径参数&qu

  • vue用addRoutes实现动态路由的示例

    之前在基于Vue实现后台系统权限控制一文中提到路由权限的实现思路,因为不喜欢在每次路由跳转的before钩子里做判断,所以在初始化Vue实例前对路由做了筛选,再用实际路由初始化Vue实例,代价是登录页需要从Vue实例中独立出来,实现上倒没什么问题,不过这种做法需要在登录和首页之间通过url跳转,感觉总是不太"优雅",实际上只要能在登录后动态修改当前实例的路由就行了,之前确实没办法,但vue-router 2.2版本新增了一个router.addRoutes(routes)方法,让动态路

  • 详解vue路由篇(动态路由、路由嵌套)

    什么是路由?网络原理中,路由指的是根据上一接口的数据包中的IP地址,查询路由表转发到另一个接口,它决定的是一个端到端的网络路径. web中,路由的概念也是类似,根据URL来将请求分配到指定的一个'端'.(即根据网址找到能处理这个URL的程序或模块) 使用vue.js构建项目,vue.js本身就可以通过组合组件来组成应用程序:当引入vue-router后,我们需要处理的是将组件(components)映射到路由(routes),然后在需要的地方进行使用渲染. 一.基础路由 1.创建vue项目,执行

  • 全面解析vue router 基本使用(动态路由,嵌套路由)

    路由,其实就是指向的意思,当我点击页面上的home按钮时,页面中就要显示home的内容,如果点击页面上的about 按钮,页面中就要显示about 的内容.Home按钮  => home 内容, about按钮 => about 内容,也可以说是一种映射. 所以在页面上有两个部分,一个是点击部分,一个是点击之后,显示内容的部分. 点击之后,怎么做到正确的对应,比如,我点击home 按钮,页面中怎么就正好能显示home的内容.这就要在js 文件中配置路由. 路由中有三个基本的概念 route,

  • vue系列之动态路由详解【原创】

    开题 最近用vue来构建了一个小项目,由于项目是以iframe的形式嵌套在别的项目中的,所以对于登录的验证就比较的麻烦,索性后端大佬们基于现在的问题提出了解决的方案,在看到他们的解决方案之前,我先画了一个比较标准的单系统的解决方案. 本文目录: 一: 设想 二: 讨论 三:实现 四:总结 一: 设想 简单解释下上图就是: 首先前端从cookie获取token,如果没有token就跳转到登录页面登录,登录验证之后生成token存在数据库中并返回给前端:前端将这个token保存下来,为了让在浏览器新

  • vue动态路由实现多级嵌套面包屑的思路与方法

    前言 最近在工作中遇到了一个问题,是关于vue动态路由多级嵌套面包屑怎么弄(不是动态路由嵌套可以尝试用 this.$route.matched方法获取到path和name集合,动态的嵌套获取不到全部具体的id) 功能比如:A列表页面路由如/a,点击任意一列进入任意一个A的详情页面名字为B,/b/03(这个是动态路由弄是吧,03就是id嘛),点击B页面任意一列,再进入B的详情页名字为C,路由如/bdetail/01;现在弄面包屑要获取到的路由是刚刚打开的,如(/a:/b/03:/bdetail/0

  • 基于vue 动态菜单 刷新空白问题的解决

    1.先确认自己在route.js 或者 main.js 中有没有使用 路由守卫vue.beforeEach和vue.addRouters() 促使页面每次刷新,重新根据后台返回数据生成动态路由,就像你在登陆时做的事情一样. 代码示范注意点: //注意:确定自己避免了路由守卫进入死循环 let oneRun = true; //通过oneRun变量控制 避免陷入死循环 router.beforeEach((to,from,next)=>{ if(oneRun){ oneRun = false;//

  • vue动态菜单、动态路由加载以及刷新踩坑实战

    目录 需求: 思路: 教训: 分享正文: 总结 需求: 从接口动态获取子菜单数据 动态加载 要求只有展开才加载子菜单数据 支持刷新,页面显示正常 思路: 一开始比较乱,思路很多.想了很多 首先路由和菜单共用一个全局route, 数据的传递也是通过store的route, 然后要考虑的俩个点就是一个就是渲染菜单和加载路由,可以在导航首位里处理路由,处理刷新. 还有一个地方就是菜单组件里展开事件里面 重新生成菜单数据,路由.大体思路差不多,做完就忘了..... 刷新的问题需要用本地缓存处理,之前一直

  • Vue插槽slot详细介绍(对比版本变化,避免踩坑)

    目录 插槽是什么? 怎么使用插槽? 基本用法 后备(默认)内容 具名插槽 作用域插槽 插槽版本变化 总结 Vue中的插槽(slot)在项目中用的也是比较多的,今天就来介绍一下插槽的基本使用以及Vue版本更新之后的插槽用法变化. 插槽是什么? 插槽就是子组件中的提供给父组件使用的一个占位符,用<slot></slot> 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML.组件等,填充的内容会替换子组件的<slot></slot>标签.简单理解就是子组

  • tomcat8改了jar加载顺序的踩坑记录

    记录一次调试经历 起因 相同的jar,服务器正常而本地起的项目一直报下图中的错. 解释 首先,这段代码是hibernate执行有参数的hql的过程中报错的,最上面那层,对string进行强转导致的. 看hql及java对象,发现,参数为string,而参数对应的java对象中的字段类型是BigDcimal.猜测可能是问题出现的原因,但相关的代码没有找到,继续看代码.调试 堆栈信息中 bind()方法的作用(和报错有关的),从 中获取type和value,对value进行强转,其中type是在设置

  • 详解Vue3如何加载动态菜单

    目录 1. 整体思路 2. 实现细节 2.1 加载细节 2.2 getInfo 2.3 generateRoutes 1. 整体思路 首先我们来梳理下整体上的实现思路,首先一点:整体思路和 vhr 一模一样. 考虑到有的小伙伴可能已经忘记 vhr 中前端动态菜单的实现思路了,因此本文再和大家分析一下. 为了确保在所有的 .vue 文件中都能访问到到菜单数据,所以选择将菜单数据存入 vuex 中,vuex 是 vue 中一个存储数据的公共地方,所有的 .vue 文件都可以从 vuex 中读取到数据

  • vue使用节流函数的踩坑实例指南

    前言 一个常见的业务场景,我们要在input搜索框输入结束后,发送相关请求,获取搜索数据.频繁的事件触发会导致接口请求过于频繁.所以需要我们对此加以限制,来禁止不必要的请求,以免资源的浪费~ 举一个

  • vue+element使用动态加载路由方式实现三级菜单页面显示的操作

    需要用到中间件的方式,这样就可以实现了我们想要的方式 publish-center.vue <template> <router-view></router-view> </template> <script> export default { } </script> <el-menu :default-active="$route.path" class="el-menu-vertical-dem

  • vue addRoutes实现动态权限路由菜单的示例

    需求 最近接手一个后台管理系统,需要实现导航菜单从后台拉取的效果:根据登录用户的权限不同分别拉出来的导航菜单也不一样,另外可操作的界面也存在区别. 问题 因为后台管理系统是准备使用vue+vue-router+element-ui+vuex的搭配来做的,可是单页应用在进入页面之前就已经将vue-router实例化并且注入vue实例中了,所以在进入登录页面的时候旧没办法在重新定制路由了.接下来各种百之谷之,发现vue-router在2.0版本中提供了addRoutes方法添加路由,希望的曙光出现.

  • vue addRoutes路由动态加载操作

    需求:增加权限控制,实现不同角色显示不同的路由导航 思路:每次登陆后请求接口返回当前角色路由 核心方法:vue-router2.2.0的addRoutes方法 + vuex 以下是我实现的获取菜单路由的方法,我将该方法的调用放在首页组件的生命钩子中,即便用户刷新浏览器清空了路由还是会重新调用接口获取,不至于会丢失.同时考虑到会有切换用户的可能,所以不将获取到的路由信息保存到cookie或者localstorage当中 获取菜单之前先判断routerState,避免多次请求, 我这里使用eleme

  • Vue Element前端应用开发之动态菜单和路由的关联处理

    概述 在我开发的很多系统里面,包括Winform混合框架.Bootstrap开发框架等系列产品中,我都倾向于动态配置菜单,并管理对应角色的菜单权限和页面权限,实现系统对用户权限的控制,菜单一般包括有名称.图标.顺序.URL连接等相关信息,对于VUE+Element 前端应用来说,应该原理上差不多,本篇随笔介绍结合服务端的动态菜单配置和本地路由的关联处理,实现动态菜单的维护和展示的处理. 1.菜单和路由的处理过程 由于Vue前端还需要引入路由这个概念,路由是我们前端可以访问到的对应路径集合,路由定

  • SpringBoot+Vue实现动态菜单的思路梳理

    目录 1. 整体思路 2. 前端渲染 3. 后端菜单生成 3.1 菜单表 3.2 菜单接口 关于 Spring Boot + Vue3 的动态菜单,松哥之前已经写了两篇文章了,这两篇文章主要是从代码上和大家分析动态菜单最终的实现方式,但是还是有小伙伴觉得没太看明白,感觉缺乏一个提纲挈领的思路,所以,今天松哥再整一篇文章和大家再来捋一捋这个问题,希望这篇文章能让小伙伴们彻底搞清楚这个问题. 1. 整体思路 首先我们来看整体思路. 光说思路大家还是云里雾里,我们结合具体的效果图来看: 最终菜单显示效

随机推荐