解決vue-router addRoutes路由不重新整理問題

暴走的snail發表於2018-12-29

發現問題

       在vue後臺管理專案中,動態路由是一個必須的功能,根據不同許可權顯示不同的路由表。通常的做法有兩種,1)前端將所有路由定義到配置檔案中,後臺返回使用者角色。前端根據使用者角色從配置檔案中去相應的路由物件生成路由表。(此種方法用的比較多,至今未發現有何問題). 2)前端路由表中只配置公共路由,後臺根據前端登入使用者返回相應的路由表。前端拿到路由表動態(addRoutes)的載入出來。正常登入沒啥問題,登入成功後頁面跳轉也可以,但是在動態新增的路由部分重新整理頁面會出現404問題。

router.beforeEach((to, from, next) => {  
if (from.query.code || to.query.code ) {    
   let time = (new Date()).valueOf()    
   localStorage.setItem('EXPIRE-TIME', time) 
 }   
  isLogin(to, from, next)
})
router.afterEach(() => {})
//判斷是否需要跳轉到登入頁
function isLogin(to, from, next) {  
     if (JSON.parse(localStorage.getItem('IS_LOGIN'))) {    ...... 
      } else if (!JSON.parse(localStorage.getItem('IS_LOGIN'))) {    
// 在免登入白名單,直接進入    
       if (whiteList.indexOf(to.path) !== -1 || to.path.search(/(^\/i\/[a-z0-9A-Z]+$)/g) > -1) {
        next()    
       } else { 
           if(to.path == '/'){        queryUserInfo(next)      
           }else{        next('/user/login')      
           }    
         }  }}
//在登入之後,進入首頁之前進行資訊獲取,與是否登入驗證
function  queryUserInfo(next) {    
   let url = `${process.env.VUE_APP_URL}/userprofile`    
   axios.get(url).then((res) => {
            if (res.data.success) {
                 ......
                //初始化頁面資料
                if(routeTable && routeTable.length > 0){
                  addRoutes(routeTable)
                  next()
                }else{
                  //從後臺獲取路由表
                  localStorage.setItem('ROUTE_TABLE',"[]")
                  let url = `${process.env.VUE_APP_URL}/api/permission/menu`
                  axios.get(url).then(res=>{
                    if(res.data.success){
                      routeTable = res.data.data
                    }
                    localStorage.setItem('ROUTE_TABLE',JSON.stringify(res.data.data))
                    addRoutes(routeTable)
                    next()
                  })
                }
            } else {
                next('/user/login')
            }
        }).
        catch(error=>{
          next('/user/login')
        })}
// 在登入之後,進入首頁之前進行路由表初始化
function initRouters(routeTable) {
 //遍歷後臺傳來的路由字串,轉換為元件物件
  let newRoutes = []
  routeTable.forEach(route => {
    let oRouter = {}
    if (route.component) {
      oRouter  = {
        path: route.path,
        component(resolve) {
          .....//將後臺返回的字串轉化為對應的元件物件
        },
        ....//路由相關資訊
        hidden: route.hidden ? route.hidden : false,
        children: route.children && route.children.length > 0 ? initRouters(route.children) : []
       }
      if( oRouter.children.length == 0){
        delete oRouter.children
      }
    }
    newRoutes.push(oRouter)
  })
  return newRoutes
}
//增加新路由到路由表
function addRoutes(routeTable){
  let newRoutes = []
  let oldRoutes = routers
  routers.forEach(item=>{
    let obj = {}
    ......//生成新的路由表
    if(item.path == '/'){
      obj.children = item.children.concat(initRouters(routeTable))
    }
    newRoutes.push(obj)
  })
  Store.commit('SET_ROUTERS',newRoutes)
  router.addRoutes(newRoutes)}
複製程式碼

2.問題分析    

 問題是頁面重新整理導致的,那我們就來分析下vue中頁面重新整理對於路由來說所走的流程。

vue載入main.js=>main.js呼叫router=>獲取瀏覽器URL=>router根據路由表找對應的元件=>找到對應的元件,載入元件(在載入元件前router.beforEach())=>顯示頁面

理清楚上面的路由載入流程則不難看出問題出在router根據路由表找對應的元件(此時後臺返回的路由資料還未載入到路由表中)

3.解決思路

 既然載入順序問題,那我們就需要一個能將後臺返回的路由資料儲存的功能(自然而然想到  localstorage)。

1)配置公共路由

2)首次登入成功將後臺返回的路由資料存在localstorage中並且動態(addRoutes)將資料新增到路由表陣列中

3)在公共路由router.js中判斷本地localstorage的ROUTER_TABLE是否存在,如果不存在則正常載入,如果存在則取出localstorage中的ROUTER_TABLE並處理放入router.js的陣列中

4)全域性導航監聽(router.beforEach(function(to,from,next){}),判斷localstorage中的路由資料是否存在,如果存在處理後放入動態路由中(addRoutes),否則請求後臺資料獲取相關資料

4.相關程式碼

1)定義router.js配置公共路由

  let routers=[
     ..........
   ]

  export default routers
2)vue router全域性導航鉤子監聽路由載入在進入動態路由對應的頁面前使用addRoutes新增路由
  router.berforEach(function(to,from,next){
     .....請求後臺,待資料返回
     axios.get(url).then(res=>{
      if(res.data.success){
        localstorage.setItem("ROUTER_TABLE")
        router.addRoutes(....)
      }
      }
  })
3)處理路由表router.js
let routeTable =localStorage.getItem("ROUTE_TABLE") ? JSON.parse(localStorage.getItem("ROUTE_TABLE")) : []
if(routeTable && routeTable.length > 0){ 
    routers =  addRoutes(routers)
}
function initRouters(routeTable) { 
//遍歷後臺傳來的路由字串,轉換為元件物件  
let newRoutes = []  
routeTable.forEach(route => {
    let oRouter = {}
    if (route.component) {
    oRouter  = {
      path: route.path,
      component(resolve) {
       ....
    }
    newRoutes.push(oRouter)
  })
  return newRoutes
  }
 //增加新路由到路由表
function addRoutes(routers){
  let newRoutes = []
  routers.forEach(item=>{
    let obj = {}
    for(let key in item){
    obj[key] = item[key]
    }
    if(item.path == '/'){
    obj.children = item.children.concat(initRouters(routeTable))
    }    newRoutes.push(obj)  })
  Store.commit('SET_ROUTERS',newRoutes)
  return newRoutes
  }
4)最請參照第一段相關程式碼


複製程式碼


相關文章