vue動態(tài)菜單、動態(tài)路由加載以及刷新踩坑實戰(zhàn)
需求:
從接口動態(tài)獲取子菜單數(shù)據(jù) 動態(tài)加載 要求只有展開才加載子菜單數(shù)據(jù) 支持刷新,頁面顯示正常

思路:
一開始比較亂,思路很多。想了很多
首先路由和菜單共用一個全局route, 數(shù)據(jù)的傳遞也是通過store的route, 然后要考慮的倆個點就是一個就是渲染菜單和加載路由,可以在導(dǎo)航首位里處理路由,處理刷新。
還有一個地方就是菜單組件里展開事件里面 重新生成菜單數(shù)據(jù),路由。大體思路差不多,做完就忘了..... 刷新的問題需要用本地緩存處理,之前一直緩存這個route 大數(shù)據(jù),但是這個localstore 緩存的只是字符串,不能緩存對象,這樣的話,菜單是出來了,動態(tài)的路由404,因為json.parse 轉(zhuǎn)出來的對象 不是真實路由數(shù)據(jù),還需要單獨處理component 這個是個函數(shù)對象,
都是坑....所以之前走了點彎路,思路沒想好。
第二天,重新整理思路,想了下,為啥要緩存整個route對象,傻是不是,動態(tài)的數(shù)據(jù)只是一部分,三級菜單...何不分開存儲,本地存儲動態(tài)菜單數(shù)據(jù),利用完整的路由模板,取出來的初始化路由對象,然后,循環(huán)菜單數(shù)據(jù),動態(tài)設(shè)置children屬性,生成一個新的完整的路由對象,addRoute不是更好嗎
想到這里,整理下完整思路
【定義全局route對象】=> 【導(dǎo)航首位判斷刷新、初始化加載 store中route為空】=> 【初始化路由和菜單】=> 【菜單展開事件里面,請求接口,拿到子菜單數(shù)據(jù),localStore 存儲菜單數(shù)據(jù),更新路由】
還有一些小坑 比如重復(fù)路由、刷新404問題、刷新白屏、異步處理...
教訓(xùn):
問題肯定能解決,折騰幾天,最后才發(fā)現(xiàn)思路最重要
思路錯誤,就是浪費時間
先想好思路,完整的實現(xiàn)路線 先干什么后干什么 其中遇到技術(shù)難點再去百度
分享正文:
暴力貼代碼!?。。。。。。。。。。?!
全局定義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 守衛(wèi)初始化,這里注釋掉
})
//路由重復(fù)的問題 解決
router.$addRoutes = (params) => {
router.matcher = new Router({ // 重置路由規(guī)則
scrollBehavior: () => ({
y: 0
})
}).matcher
router.addRoutes(params) // 添加路由
}
export default router
//監(jiān)聽路由守衛(wèi) 生成動態(tài)路由
router.beforeEach((to, from, next) => {
const routes = store.state.app.routes
console.error('beforeEach 守衛(wèi)執(zhí)行了')
//處理首次加載 刷新
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()
})
/**
* 更新三級子菜單 路由元數(shù)據(jù)
*/
export const updateIPChildRoutes = function(routes, path, children) {
return setRouteArrayChildren(routes, path, children)
}
/**
* 根據(jù)父菜單加載子菜單
* @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
}
//動態(tài)添加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'
// 設(shè)置路由緩存值
const localRouteKey = "LOCALROUTESET";
/**
* currentPath: '' //當(dāng)前訪問的路由路徑
* routeData: [], //存儲的完整路由數(shù)據(jù)(僅加載菜單可用)
* asyncRouteData: [] //動態(tài)的路由數(shù)據(jù)(生成新路由使用)
* {
* 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);
}
/**
* 本地緩存 轉(zhuǎn)化成路由元數(shù)據(jù)
* @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
}
/**
* 設(shè)置路由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)
//動態(tài)生成監(jiān)控三級菜單/路由
const ipAddrKeys = []
grafanaAddrs.forEach(
addr => {
if (addr.matchup) {
ipAddrKeys.push(addr.key)
}
}
)
if (path && ipAddrKeys.includes(path)) {
generateIPChildRoutes(path)
.then(ipAddrs => {
if (isArray(ipAddrs)) {
//緩存動態(tài)路由數(shù)據(jù)
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])
}
})
}
}
其他代碼 不是核心的 不貼了
總結(jié)
到此這篇關(guān)于vue動態(tài)菜單、動態(tài)路由加載以及刷新踩坑的文章就介紹到這了,更多相關(guān)vue動態(tài)菜單、動態(tài)路由加載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Element Notification通知的實現(xiàn)示例
這篇文章主要介紹了Element Notification通知的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
vue 實現(xiàn)根據(jù)data中的屬性值來設(shè)置不同的樣式
這篇文章主要介紹了vue 實現(xiàn)根據(jù)data中的屬性值來設(shè)置不同的樣式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08
Vue純前端如何實現(xiàn)導(dǎo)出簡單Excel表格的功能
這篇文章主要介紹了如何在Vue項目中使用vue-json-excel插件實現(xiàn)Excel表格的導(dǎo)出功能,包括安裝依賴、引入插件、使用組件、設(shè)置表頭和數(shù)據(jù)、處理空數(shù)據(jù)情況、源代碼修改以解決常見問題,需要的朋友可以參考下2025-01-01

