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动态菜单、动态路由加载内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!