vue 動態選單以及動態路由載入、重新整理採的坑

創業男生發表於2021-10-15

需求:


從介面動態獲取子選單資料 動態載入 要求只有展開才載入子選單資料 支援重新整理,頁面顯示正常

 

 

 

思路:

一開始比較亂,思路很多。想了很多

首先路由和選單共用一個全域性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])

          }
         })
      }
    }

其他程式碼 不是核心的 不貼了 

 

 

 

 

相關文章