現(xiàn)在寫(xiě)后臺(tái)管理都是直接CV之前的模版喷市,最近正好在看vue-admin的后臺(tái)項(xiàng)目剔氏,干脆模仿著重新搭一個(gè),可能太久沒(méi)寫(xiě)過(guò)這么多bug了吧遣妥,所以它想讓我重新懷念一下擅编,結(jié)果可直接跳到最后查看(我沒(méi)有后臺(tái)接口,也懶得模擬了箫踩,所以直接全寫(xiě)的死數(shù)據(jù))
一開(kāi)始的路由攔截如下:
if (to.path === '/login') {
next({ path: '/' })
} else {
// permission/generateRoutes里面我遍歷生成了自己想要的目標(biāo)樹(shù)爱态,大家根據(jù)自己的項(xiàng)目需求進(jìn)行編寫(xiě)即可
const accessRoutes = await store.dispatch(
'permission/generateRoutes',
)
router.addRoutes(accessRoutes)
next({ ...to, replace: true })
}
后來(lái)爆了個(gè)死循環(huán),懷疑是沒(méi)匹配到將要跳轉(zhuǎn)到路由然后一直去攔截匹配造成的然后將最后一行直接改為next()排除是否是此原因
...
next()
ok境钟,死循環(huán)消失锦担,那就應(yīng)該是這行的問(wèn)題了,不過(guò)發(fā)現(xiàn)出現(xiàn)警告?vue-router.esm.js?8c4f:16 [vue-router] router.addRoutes() is deprecated and has been removed in Vue Router 4. Use router.addRoute() instead.還有重復(fù)命名路由
再次修改為如下:
const accessRoutes = await store.dispatch(
'permission/generateRoutes',
)
console.log(accessRoutes);
router.addRoute(accessRoutes);
router.options.isAddAsyncMenuData=true;
next()
結(jié)果慨削,哎嘛不警告了直接報(bào)錯(cuò)洞渔,報(bào)錯(cuò)內(nèi)容:[vue-router] "path" is required in a route configuration.
然后去看了下文檔
后將代碼改為:
const accessRoutes = await store.dispatch(
'permission/generateRoutes',
)
for(let i=0,length=accessRoutes.length;i<length;i+=1){
const element=accessRoutes[i]
router.addRoute(element)
}
router.options.isAddAsyncMenuData=true;
// 如果 addRoutes 并未完成,路由守衛(wèi)會(huì)一層一層的執(zhí)行執(zhí)行缚态,直到 addRoutes 完成磁椒,找到對(duì)應(yīng)的路由
next({ ...to, replace: true })
此時(shí)死循環(huán)依然存在,后來(lái)發(fā)現(xiàn)這塊的邏輯判斷寫(xiě)的有問(wèn)題玫芦,會(huì)一直添加一直循環(huán)浆熔,to就會(huì)一直匹配。
此處說(shuō)一下我們有時(shí)候會(huì)看到類(lèi)似這種next()桥帆、next('/login') 医增、 next(to) 或者 next({ ...to, replace: true }),這幾者有什么不同呢老虫?
其實(shí)這些就只有next()是放行通過(guò)叶骨,其余都是中斷當(dāng)前導(dǎo)航,執(zhí)行新的導(dǎo)航祈匙。記住忽刽,只有next()是通過(guò)!只有next()是通過(guò)菊卷!只有next()是通過(guò)缔恳!其他都是中斷宝剖,然后執(zhí)行新導(dǎo)航洁闰,可能有小伙伴會(huì)反駁那我平時(shí)寫(xiě)類(lèi)似于next('/home')也會(huì)跳轉(zhuǎn)啊,你騙人呢吧万细。乖乖扑眉,如果跳轉(zhuǎn)了可能是你外面又加了層其他判斷纸泄。舉例:
如果你直接在攔截器里面寫(xiě)next('/login'),
beforeEach((to, from, next) => {
next('/login')
}
它并不會(huì)跳轉(zhuǎn)而是會(huì)一直循環(huán)下去腰素,而是相當(dāng)于
beforeEach((to, from, next) => {
beforeEach(('/login', from, next) => {
beforeEach(('/login', from, next) => {
beforeEach(('/login', from, next) => {
beforeEac... // 一直循環(huán)下去...... , 因?yàn)槲覀儧](méi)有使用 next() 放行
}
}
}
}
再舉例聘裁,如果我們想去往home首頁(yè)的話(huà)就去往login,非home首頁(yè)可放行
beforeEach((to, from, next) => {
if(to.path === '/home') {
next('/login')
} else {
// 如果要去的地方不是 /home 弓千, 就放行
next()
}
}
那這個(gè)有什么說(shuō)法呢衡便?
我本來(lái)要去/home路由,因此執(zhí)行了第一次 beforeEach((to, from, next)洋访,但是這個(gè)路由守衛(wèi)中判斷了如果要去的地方是'/home'镣陕,就執(zhí)行next('/login'),路由守衛(wèi)再攔截姻政,路徑是什么呆抑?是home首頁(yè)嗎?不是汁展,是login鹊碍,ok,放行食绿,你可以走了侈咕。
再說(shuō)說(shuō)為什么用next({ ...to, replace: true })。
在addRoute()之后第一次訪(fǎng)問(wèn)被添加的路由會(huì)白屏器紧,這是因?yàn)閯倓俛ddRoute()就立刻訪(fǎng)問(wèn)被添加的路由乎完,然而此時(shí)addRoute()沒(méi)有執(zhí)行結(jié)束,我這個(gè)錢(qián)還沒(méi)打過(guò)來(lái)還在路上你就問(wèn)我借錢(qián)我沒(méi)有你懵不懵我自己個(gè)兒都懵對(duì)吧品洛,因而找不到剛剛被添加的路由會(huì)導(dǎo)致白屏树姨。因此需要從新訪(fǎng)問(wèn)一次路由才行。
此時(shí)就要使用next({ ...to, replace: true })來(lái)確保addRoute()時(shí)動(dòng)態(tài)添加的路由已經(jīng)被完全加載上去桥状。
replace: true只是一個(gè)設(shè)置信息帽揪,告訴VUE本次操作后,不能通過(guò)瀏覽器后退按鈕辅斟,返回前一個(gè)路由转晰。當(dāng)然如果你有大無(wú)畏精神你可以不寫(xiě),而是只寫(xiě)next({ ...to })士飒,當(dāng)然你也要做好用戶(hù)在addRoutes()還沒(méi)有完成的時(shí)候查邢,點(diǎn)擊瀏覽器回退按鈕的準(zhǔn)備了。
它其實(shí)就是用to去匹配找對(duì)應(yīng)的路由酵幕,如果沒(méi)找到就再執(zhí)行一次beforeEach((to, from, next)直到其中的next({ ...to})能找到對(duì)應(yīng)的路由為止扰藕。
也就是說(shuō)此時(shí)addRoute()已經(jīng)完成,我們?nèi)フ覍?duì)應(yīng)路由芳撒,找到對(duì)應(yīng)路由后邓深,接下來(lái)將執(zhí)行前往對(duì)應(yīng)路由的路由攔截未桥,因此需要用代碼來(lái)判斷這一次是否就是前往對(duì)應(yīng)路由的beforeEach((to, from, next),如果是芥备,就執(zhí)行next()放行冬耿。如果沒(méi)有找到對(duì)應(yīng)的呢?死循環(huán)唄萌壳!所以亦镶,老天爺啊你不光要正確的把路由加上,你還得給它留個(gè)正確的出口啊乖乖
ok,下面是我最終寫(xiě)好的路由攔截
// 存在 token 說(shuō)明已經(jīng)登錄(其實(shí)我沒(méi)有接口去請(qǐng)求袱瓮,但是為了模版的完整性防止之后正常運(yùn)用的項(xiàng)目中還要再修改染乌,我本地寫(xiě)了個(gè)死的token值)
if (getToken()) {
// 登錄過(guò)就不用再訪(fǎng)問(wèn)登錄界面,去往主頁(yè)即可
if (to.path === '/login') {
next({ path: '/' })
}
// 保存在store中路由不為空則放行 (如果執(zhí)行了刷新操作懂讯,則 store 里的路由為空荷憋,此時(shí)需要重新添加路由)
if (store.getters.permission_routes.length || to.name != null) {
//放行
next()
} else {
const accessRoutes = await store.dispatch(
'permission/generateRoutes',
)
for(let i=0,length=accessRoutes.length;i<length;i+=1){
const element=accessRoutes[i]
router.addRoute(element)
}
router.options.isAddAsyncMenuData=true;
// 如果 addRoutes 并未完成,路由守衛(wèi)會(huì)一層一層的執(zhí)行執(zhí)行褐望,直到 addRoutes 完成勒庄,找到對(duì)應(yīng)的路由
next({ ...to, replace: true })
}
} else {
// 未登錄時(shí),whiteList是我設(shè)置的免登錄白名單就是不登錄沒(méi)有token也能進(jìn)入
if (whiteList.indexOf(to.path) !== -1) {
// 在免登錄白名單中瘫里,直接進(jìn)入
next()
} else {
// 其他沒(méi)有訪(fǎng)問(wèn)權(quán)限的頁(yè)面將重定向到登錄頁(yè)面
next(`/login?redirect=${to.path}`)
}
}