Vue?Router4路由導(dǎo)航守衛(wèi)實(shí)例全面解析
前言
在寫Vue項(xiàng)目的時(shí)候,少不了使用路由vue-router,而路由守衛(wèi)是vue-router中一個(gè)非常重要的概念,也是非常重要的技巧。它能夠很好的幫助開(kāi)發(fā)者“監(jiān)視”每一個(gè)跳轉(zhuǎn)的路由。
一、什么是導(dǎo)航守衛(wèi)
正如其名,vue-router 提供的導(dǎo)航守衛(wèi)主要用來(lái)通過(guò)跳轉(zhuǎn)或取消的方式守衛(wèi)導(dǎo)航。這里有很多方式植入路由導(dǎo)航中:全局的,單個(gè)路由獨(dú)享的,或者組件級(jí)的。Vue3對(duì)應(yīng)的是vue-router4,下面就對(duì)它們進(jìn)行一一講解。
二、全局前置守衛(wèi)
最常用的是全局前置導(dǎo)航守衛(wèi),你可以使用 router.beforeEach
注冊(cè)一個(gè)全局前置守衛(wèi):
const router = createRouter({ ... }) router.beforeEach((to, from) => { // ... // 返回 false 以取消導(dǎo)航 return false })
當(dāng)一個(gè)導(dǎo)航觸發(fā)時(shí),全局前置守衛(wèi)按照創(chuàng)建順序調(diào)用。守衛(wèi)是異步解析執(zhí)行,此時(shí)導(dǎo)航在所有守衛(wèi) resolve 完之前一直處于等待中。
每個(gè)守衛(wèi)方法接收兩個(gè)參數(shù):
to
: 即將要進(jìn)入的目標(biāo)(將要前往的路由)from
: 當(dāng)前導(dǎo)航正要離開(kāi)的路由
可以返回的值如下:
false
: 取消當(dāng)前的導(dǎo)航。如果瀏覽器的 URL 改變了(可能是用戶手動(dòng)或者瀏覽器后退按鈕),那么 URL 地址會(huì)重置到from
路由對(duì)應(yīng)的地址。- 路由地址: 通過(guò)一個(gè)路由地址跳轉(zhuǎn)到一個(gè)不同的地址,就像調(diào)用
router.push()
一樣,你可以設(shè)置諸如replace: true
或name: 'home'
之類的配置。當(dāng)前的導(dǎo)航被中斷,然后進(jìn)行一個(gè)新的導(dǎo)航,就和from
一樣。
router.beforeEach(async (to, from) => { // 檢查用戶是否已登錄,避免無(wú)限重定向 if ( !isAuthenticated && to.name !== 'Login' ) { // 將用戶重定向到登錄頁(yè)面 return { name: 'Login' } } })
如果遇到了意料之外的情況,可能會(huì)拋出一個(gè) Error
。這會(huì)取消導(dǎo)航并且調(diào)用router.onError()
注冊(cè)過(guò)的回調(diào)。
如果什么都沒(méi)有,undefined
或返回 true
,則導(dǎo)航是有效的,并調(diào)用下一個(gè)導(dǎo)航守衛(wèi)
以上所有都同 async
函數(shù) 和 Promise 工作方式一樣:
router.beforeEach(async (to, from) => { // canUserAccess() 返回 `true` 或 `false` const canAccess = await canUserAccess(to) if (!canAccess) return '/login' })
可選的第三個(gè)參數(shù) next
在之前的 Vue Router 版本中,也是可以使用 第三個(gè)參數(shù) next
的。這是一個(gè)常見(jiàn)的錯(cuò)誤來(lái)源,可以通過(guò) RFC
來(lái)消除錯(cuò)誤。然而,它仍然是被支持的,這意味著你可以向任何導(dǎo)航守衛(wèi)傳遞第三個(gè)參數(shù)。在這種情況下,確保 next
在任何給定的導(dǎo)航守衛(wèi)中都被嚴(yán)格調(diào)用一次。它可以出現(xiàn)多于一次,但是只能在所有的邏輯路徑都不重疊的情況下,否則鉤子永遠(yuǎn)都不會(huì)被解析或報(bào)錯(cuò)。這里有一個(gè)在用戶未能驗(yàn)證身份時(shí)重定向到/login
的錯(cuò)誤用例:
// BAD router.beforeEach((to, from, next) => { if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) // 如果用戶未能驗(yàn)證身份,則 `next` 會(huì)被調(diào)用兩次 next() })
下面是正確的版本:
// GOOD router.beforeEach((to, from, next) => { if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' }) else next() })
三、全局解析守衛(wèi)
你可以用 router.beforeResolve
注冊(cè)一個(gè)全局守衛(wèi)。這和 router.beforeEach
類似,因?yàn)樗?strong>每次導(dǎo)航時(shí)都會(huì)觸發(fā),但是確保在導(dǎo)航被確認(rèn)之前,同時(shí)在所有組件內(nèi)守衛(wèi)和異步路由組件被解析之后,解析守衛(wèi)就被正確調(diào)用。這里有一個(gè)例子,確保用戶可以訪問(wèn) 自定義 meta
屬性 requiresCamera
的路由:
router.beforeResolve(async to => { if (to.meta.requiresCamera) { try { await askForCameraPermission() } catch (error) { if (error instanceof NotAllowedError) { // ... 處理錯(cuò)誤,然后取消導(dǎo)航 return false } else { // 意料之外的錯(cuò)誤,取消導(dǎo)航并把錯(cuò)誤傳給全局處理器 throw error } } } })
router.beforeResolve
是獲取數(shù)據(jù)或執(zhí)行任何其他操作(如果用戶無(wú)法進(jìn)入頁(yè)面時(shí)你希望避免執(zhí)行的操作)的理想位置。
四、全局后置鉤子
你也可以注冊(cè)全局后置鉤子,當(dāng)跳轉(zhuǎn)到了當(dāng)面界面,做一些操作。然而和守衛(wèi)不同的是,這些鉤子不會(huì)接受 next
函數(shù)也不會(huì)改變導(dǎo)航本身:
router.afterEach((to, from) => { sendToAnalytics(to.fullPath) })
它們對(duì)于分析、更改頁(yè)面標(biāo)題、聲明頁(yè)面等輔助功能以及許多其他事情都很有用。
router.after((to,from)=>{ document.title = to.meta.title })
它們也反映了 navigation failures 作為第三個(gè)參數(shù):
router.afterEach((to, from, failure) => { if (!failure) sendToAnalytics(to.fullPath) })
了解更多關(guān)于 navigation failures 的信息在它的指南中。
五、路由獨(dú)享的守衛(wèi)
你可以直接在路由配置上定義 beforeEnter
守衛(wèi):
const routes = [ { path: '/users/:id', component: UserDetails, beforeEnter: (to, from) => { // reject the navigation return false }, }, ]
beforeEnter
守衛(wèi) 只在進(jìn)入路由時(shí)觸發(fā),不會(huì)在 params
、query
或 hash
改變時(shí)觸發(fā)。例如,從 /users/2
進(jìn)入到 /users/3
或者從 /users/2#info
進(jìn)入到 /users/2#projects
。它們只有在 從一個(gè)不同的 路由導(dǎo)航時(shí),才會(huì)被觸發(fā)。
你也可以將一個(gè)函數(shù)數(shù)組傳遞給 beforeEnter
,這在為不同的路由重用守衛(wèi)時(shí)很有用:
function removeQueryParams(to) { if (Object.keys(to.query).length) return { path: to.path, query: {}, hash: to.hash } } function removeHash(to) { if (to.hash) return { path: to.path, query: to.query, hash: '' } } const routes = [ { path: '/users/:id', component: UserDetails, beforeEnter: [removeQueryParams, removeHash], }, { path: '/about', component: UserDetails, beforeEnter: [removeQueryParams], }, ]
請(qǐng)注意,你也可以通過(guò)使用路徑 meta 字段和全局導(dǎo)航守衛(wèi)來(lái)實(shí)現(xiàn)類似的行為
六、組件內(nèi)的守衛(wèi)
最后,你可以在路由組件內(nèi)直接定義路由導(dǎo)航守衛(wèi)(傳遞給路由配置的)
可用的配置 API
你可以為路由組件添加以下配置:
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave
const UserDetails = { template: `...`, beforeRouteEnter(to, from) { // 在渲染該組件的對(duì)應(yīng)路由被驗(yàn)證前調(diào)用 // 不能獲取組件實(shí)例 `this` ! // 因?yàn)楫?dāng)守衛(wèi)執(zhí)行時(shí),組件實(shí)例還沒(méi)被創(chuàng)建! }, beforeRouteUpdate(to, from) { // 在當(dāng)前路由改變,但是該組件被復(fù)用時(shí)調(diào)用 // 舉例來(lái)說(shuō),對(duì)于一個(gè)帶有動(dòng)態(tài)參數(shù)的路徑 `/users/:id`,在 `/users/1` 和 `/users/2` 之間跳轉(zhuǎn)的時(shí)候, // 由于會(huì)渲染同樣的 `UserDetails` 組件,因此組件實(shí)例會(huì)被復(fù)用。而這個(gè)鉤子就會(huì)在這個(gè)情況下被調(diào)用。 // 因?yàn)樵谶@種情況發(fā)生的時(shí)候,組件已經(jīng)掛載好了,導(dǎo)航守衛(wèi)可以訪問(wèn)組件實(shí)例 `this` }, beforeRouteLeave(to, from) { // 在導(dǎo)航離開(kāi)渲染該組件的對(duì)應(yīng)路由時(shí)調(diào)用 // 與 `beforeRouteUpdate` 一樣,它可以訪問(wèn)組件實(shí)例 `this` }, }
beforeRouteEnter
守衛(wèi) 不能 訪問(wèn) this
,因?yàn)槭匦l(wèi)在導(dǎo)航確認(rèn)前被調(diào)用,因此即將登場(chǎng)的新組件還沒(méi)被創(chuàng)建。
不過(guò),你可以通過(guò)傳一個(gè)回調(diào)給 next
來(lái)訪問(wèn)組件實(shí)例。在導(dǎo)航被確認(rèn)的時(shí)候執(zhí)行回調(diào),并且把組件實(shí)例作為回調(diào)方法的參數(shù):
beforeRouteEnter (to, from, next) { next(vm => { // 通過(guò) `vm` 訪問(wèn)組件實(shí)例 }) }
注意 beforeRouteEnter
是支持給 next
傳遞回調(diào)的唯一守衛(wèi)。對(duì)于 beforeRouteUpdate
和 beforeRouteLeave
來(lái)說(shuō),this
已經(jīng)可用了,所以不支持 傳遞回調(diào),因?yàn)闆](méi)有必要了:
beforeRouteUpdate (to, from) { // just use `this` this.name = to.params.name }
這個(gè) 離開(kāi)守衛(wèi) 通常用來(lái)預(yù)防用戶在還未保存修改前突然離開(kāi)。該導(dǎo)航可以通過(guò)返回 false
來(lái)取消。
beforeRouteLeave (to, from) { const answer = window.confirm('Do you really want to leave? you have unsaved changes!') if (!answer) return false }
使用組合 API
如果你正在 使用組合 API 和 setup
函數(shù)來(lái)編寫組件,你可以通過(guò) onBeforeRouteUpdate
和 onBeforeRouteLeave
分別添加 update 和 leave 守衛(wèi)。
七、完整的導(dǎo)航解析流程
- 導(dǎo)航被觸發(fā)。
- 在失活的組件里調(diào)用
beforeRouteLeave
守衛(wèi)。 - 調(diào)用全局的
beforeEach
守衛(wèi)。 - 在重用的組件里調(diào)用
beforeRouteUpdate
守衛(wèi)(2.2+)。 - 在路由配置里調(diào)用
beforeEnter
。 - 解析異步路由組件。
- 在被激活的組件里調(diào)用
beforeRouteEnter
。 - 調(diào)用全局的
beforeResolve
守衛(wèi)(2.5+)。 - 導(dǎo)航被確認(rèn)。
- 調(diào)用全局的
afterEach
鉤子。 - 觸發(fā) DOM 更新。
- 調(diào)用
beforeRouteEnter
守衛(wèi)中傳給next
的回調(diào)函數(shù),創(chuàng)建好的組件實(shí)例會(huì)作為回調(diào)函數(shù)的參數(shù)傳入。
以上就是Vue Router4路由導(dǎo)航守衛(wèi)實(shí)例全面解析的詳細(xì)內(nèi)容,更多關(guān)于Vue Router4路由導(dǎo)航的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue動(dòng)態(tài)路由匹配和路由懶加載多種方法詳解
這篇文章主要介紹了vue動(dòng)態(tài)路由匹配和路由懶加載,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06使用idea創(chuàng)建第一個(gè)Vue項(xiàng)目
最近在學(xué)習(xí)vue,本文主要介紹了使用idea創(chuàng)建第一個(gè)Vue項(xiàng)目,文中根據(jù)圖文介紹的十分詳盡,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03淺談element中InfiniteScroll按需引入的一點(diǎn)注意事項(xiàng)
這篇文章主要介紹了淺談element中InfiniteScroll按需引入的一點(diǎn)注意事項(xiàng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06Vue組件tree實(shí)現(xiàn)樹(shù)形菜單
這篇文章主要為大家詳細(xì)介紹了Vue組件tree實(shí)現(xiàn)樹(shù)形菜單,小巧實(shí)用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04Vuex報(bào)錯(cuò)之[vuex] unknown mutation type: han
這篇文章主要介紹了Vuex報(bào)錯(cuò)之[vuex] unknown mutation type: handlePower問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07Vue3?中路由Vue?Router?的使用實(shí)例詳解
vue-router是vue.js官方給出的路由解決方案,能夠輕松的管理SPA項(xiàng)目中組件的切換,這篇文章主要介紹了Vue3?中路由Vue?Router?的使用,需要的朋友可以參考下2023-02-02vue文件上傳Required request part ‘file‘ is&n
這篇文章主要介紹了vue文件上傳Required request part ‘file‘ is not present問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11解決ant-design-vue安裝報(bào)錯(cuò)的問(wèn)題
這篇文章主要介紹了解決ant-design-vue安裝報(bào)錯(cuò)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12