發現問題
在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)最請參照第一段相關程式碼
複製程式碼