Vue動態載入路由進入頁面白屏或beforeEach死迴圈採坑記錄
開發的哥們兒從網上找了一個簡單的框架,通過auth的配置來決定什麼許可權可以進入到什麼頁面。具體操作就是登陸獲取使用者的許可權,然後遍歷本地的總路由表匹配許可權返回當前使用者可以訪問的路由表。網上大多數的方案是從後端介面獲取許可權和路由表。此為兩者之間的差異,但是不影響填坑方案的可行性
先放上本地總路由表部分程式碼,就是在這個表中根據許可權進一步遍歷篩選的,通過硬編碼的方式寫在專案中,auth[]內用數字、用中文、用單詞也都是可以的
const routerList = [{
path: '/',
component: Layout,
redirect: '/dashboard',
auth: [0, 1, 2],
meta: {
title: '首頁',
icon: 'dashboard'
},
children: [{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: {
title: '首頁',
icon: 'dashboard'
} ,
auth: [0, 1, 2]
}]
},
{
path: '/enterprises',
component: Layout,
name: 'Enterprises',
redirect: 'noRedirect',
auth: [0, 1, 2],
meta: {
title: '智慧園區',
icon: 'el-icon-office-building'
},
children: [
{
path: 'huanjingjiance',
name: 'huanjingjiance',
component: () => import('@/views/environment/monitor'),
auth: [0, 2],
meta: {
title: '環境監測'
},
}
]
},
{
path: '/user',
component: Layout,
name: 'User',
redirect: 'noRedirect',
auth: [0, 1],
meta: {
title: '人員資訊',
icon: 'el-icon-user'
},
children: [{
path: 'index',
name: 'Index',
component: () => import('@/views/user/index'),
meta: {
title: '個人資訊'
},
auth: [0, 1],
}]
}
]
首先在路由中需要指定登入的頁面,這個是不分許可權的,所以不需要動態獲取
const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
...dynamicRouter
]
var createRouter = () => new Router({
mode: 'history',
scrollBehavior: () => ({
y: 0
}),
routes: constantRoutes
})
其次,是登入完,動態獲取路由,這裡的邏輯是獲取使用者許可權標識,通過filterRouter方法遍歷獲得當前許可權路由表。看到很多的部落格寫 router.addRoutes(dynamicRouter); 但是實際開發過程中會發現登入完進入主頁是白屏的,最終再加上 router.options.routes = dynamicRouter; 解決問題
export function resetRouter(roleType) {
const newRouter = createRouter()
router.matcher = newRouter.matcher;
//從介面動態獲取路由表則可以省略這一步
dynamicRouter = filterRouter(routerList, roleType);
router.options.routes = dynamicRouter;
router.addRoutes(dynamicRouter);
store.dispatch('dynamicRouter/dynamicRouter', dynamicRouter);
}
export function filterRouter(Allrourer, roleType) {
const res = Allrourer.filter(item => {
return item.auth.indexOf(roleType) !== -1
})
res.length && res.forEach(item => {
if (item.children) {
item.children = filterRouter(item.children, roleType)
}
})
return res
}
解決問題以後,開開心心進入到主頁,這個時候激動的小手按了一下F5,突然又白屏了…
原來是路由守衛router.beforeEach裡面router是空的,所以需要再次addRoutes一遍。這裡我通過判斷Vuex裡面有沒有存dynamicRouter路由表來決定需不需要重新addRoutes,如果不寫 store.dispatch(‘dynamicRouter/dynamicRouter’, dynamicRouter); 就會碰到一直死迴圈的情況(其實是一直在執行else裡面的方法),所以需要注意。這裡引用其他博主形象的說明解釋一下,為什麼會一直往else裡面跑,原文《VUE 路由守衛 next() / next({ …to, replace: true }) / next(‘/‘) 說明》
在addRoutes()之後第一次訪問被新增的路由會白屏,這是因為剛剛addRoutes()就立刻訪問被新增的路由,然而此時addRoutes()沒有執行結束,因而找不到剛剛被新增的路由導致白屏。因此需要從新訪問一次路由才行。
該如何解決這個問題 ?
此時就要使用next({ …to, replace: true })來確保addRoutes()時動態新增的路由已經被完全載入上去。
next({ …to, replace: true })中的replace: true只是一個設定資訊,告訴VUE本次操作後,不能通過瀏覽器後退按鈕,返回前一個路由。
因此next({ …to, replace: true })可以寫成next({ …to }),不過你應該不希望使用者在addRoutes()還沒有完成的時候,可以點選瀏覽器回退按鈕搞事情吧。
其實next({ …to })的執行很簡單,它會判斷:
如果引數to不能找到對應的路由的話,就再執行一次beforeEach((to, from, next)直到其中的next({ …to})能找到對應的路由為止。
也就是說此時addRoutes()已經完成啦,找到對應的路由之後,接下來將執行前往對應路由的beforeEach((to, from, next) ,因此需要用程式碼來判斷這一次是否就是前往對應路由的beforeEach((to, from, next),如果是,就執行next()放行。
綜上所述,如果我不寫 store.dispatch(‘dynamicRouter/dynamicRouter’, dynamicRouter); 那就一直在else裡面,那怕載入了路由表也沒用
if (store.getters.dynamicRouter.length > 0) {
next();
} else {
//也可以根據許可權從後端介面獲取
var dynamicRouter = filterRouter(routerList, roleType);
//重新執行beforeEach時防止死迴圈
store.dispatch('dynamicRouter/dynamicRouter', dynamicRouter);
router.options.routes = dynamicRouter;
router.addRoutes(dynamicRouter);
next({...to, replace: true});
}
打完收工,祝各位小夥伴上線順利,工作開心!