Vue中的路由導(dǎo)航守衛(wèi)導(dǎo)航解析流程
正如其名,vue-router 提供的導(dǎo)航守衛(wèi)主要用來通過跳轉(zhuǎn)或取消的方式守衛(wèi)導(dǎo)航。這里有很多方式植入路由導(dǎo)航中:全局的,單個路由獨(dú)享的,或者組件級的。
全局前置守衛(wèi)
你可以使用 router.beforeEach 注冊一個全局前置守衛(wèi):
const router = createRouter({ ... })
router.beforeEach((to, from) => {
// ...
// 返回 false 以取消導(dǎo)航
return false
})
當(dāng)一個導(dǎo)航觸發(fā)時,全局前置守衛(wèi)按照創(chuàng)建順序調(diào)用。守衛(wèi)是異步解析執(zhí)行,此時導(dǎo)航在所有守衛(wèi) resolve 完之前一直處于等待中。
每個守衛(wèi)方法接收兩個參數(shù):
to: 即將要進(jìn)入的目標(biāo)from: 當(dāng)前導(dǎo)航正要離開的路由
可以返回的值如下:
false: 取消當(dāng)前的導(dǎo)航。如果瀏覽器的 URL 改變了(可能是用戶手動或者瀏覽器后退按鈕),那么 URL 地址會重置到from路由對應(yīng)的地址。- 一個路由地址: 通過一個路由地址跳轉(zhuǎn)到一個不同的地址,就像你調(diào)用
router.push()一樣,你可以設(shè)置諸如replace: true或name: 'home'之類的配置。當(dāng)前的導(dǎo)航被中斷,然后進(jìn)行一個新的導(dǎo)航,就和from一樣。
router.beforeEach(async (to, from) => {
if (
// 檢查用戶是否已登錄
!isAuthenticated &&
// ?? 避免無限重定向
to.name !== 'Login'
) {
// 將用戶重定向到登錄頁面
return { name: 'Login' }
}
})
如果遇到了意料之外的情況,可能會拋出一個 Error。這會取消導(dǎo)航并且調(diào)用 router.onError() 注冊過的回調(diào)。
如果什么都沒有,undefined 或返回 true,則導(dǎo)航是有效的,并調(diào)用下一個導(dǎo)航守衛(wèi)
以上所有都同 async 函數(shù) 和 Promise 工作方式一樣:
router.beforeEach(async (to, from) => {
// canUserAccess() 返回 `true` 或 `false`
const canAccess = await canUserAccess(to)
if (!canAccess) return '/login'
})
可選的第三個參數(shù) next
在之前的 Vue Router 版本中,也是可以使用 第三個參數(shù) next 的。這是一個常見的錯誤來源,可以通過 RFC 來消除錯誤。然而,它仍然是被支持的,這意味著你可以向任何導(dǎo)航守衛(wèi)傳遞第三個參數(shù)。在這種情況下,確保 next 在任何給定的導(dǎo)航守衛(wèi)中都被嚴(yán)格調(diào)用一次。它可以出現(xiàn)多于一次,但是只能在所有的邏輯路徑都不重疊的情況下,否則鉤子永遠(yuǎn)都不會被解析或報(bào)錯。這里有一個在用戶未能驗(yàn)證身份時重定向到/login的錯誤用例:
// BAD
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
// 如果用戶未能驗(yàn)證身份,則 `next` 會被調(diào)用兩次
next()
})
下面是正確的版本:
// GOOD
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
else next()
})
全局解析守衛(wèi)
你可以用 router.beforeResolve 注冊一個全局守衛(wèi)。這和 router.beforeEach 類似,因?yàn)樗?每次導(dǎo)航時都會觸發(fā),但是確保在導(dǎo)航被確認(rèn)之前,同時在所有組件內(nèi)守衛(wèi)和異步路由組件被解析之后,解析守衛(wèi)就被正確調(diào)用。這里有一個例子,確保用戶可以訪問自定義 meta屬性 requiresCamera 的路由:
路由元信息
有時,你可能希望將任意信息附加到路由上,如過渡名稱、誰可以訪問路由等。這些事情可以通過接收屬性對象的meta屬性來實(shí)現(xiàn),并且它可以在路由地址和導(dǎo)航守衛(wèi)上都被訪問到。定義路由的時候你可以這樣配置 meta 字段:
const routes = [
{
path: '/posts',
component: PostsLayout,
children: [
{
path: 'new',
component: PostsNew,
// 只有經(jīng)過身份驗(yàn)證的用戶才能創(chuàng)建帖子
meta: { requiresAuth: true }
},
{
path: ':id',
component: PostsDetail
// 任何人都可以閱讀文章
meta: { requiresAuth: false }
}
]
}
]
那么如何訪問這個 meta 字段呢?
首先,我們稱呼 routes 配置中的每個路由對象為 路由記錄。路由記錄可以是嵌套的,因此,當(dāng)一個路由匹配成功后,它可能匹配多個路由記錄。
例如,根據(jù)上面的路由配置,/posts/new 這個 URL 將會匹配父路由記錄 (path: '/posts') 以及子路由記錄 (path: 'new')。
一個路由匹配到的所有路由記錄會暴露為 $route 對象(還有在導(dǎo)航守衛(wèi)中的路由對象)的$route.matched 數(shù)組。我們需要遍歷這個數(shù)組來檢查路由記錄中的 meta 字段,但是 Vue Router 還為你提供了一個 $route.meta 方法,它是一個非遞歸合并所有 meta 字段的(從父字段到子字段)的方法。這意味著你可以簡單地寫
router.beforeEach((to, from) => {
// 而不是去檢查每條路由記錄
// to.matched.some(record => record.meta.requiresAuth)
if (to.meta.requiresAuth && !auth.isLoggedIn()) {
// 此路由需要授權(quán),請檢查是否已登錄
// 如果沒有,則重定向到登錄頁面
return {
path: '/login',
// 保存我們所在的位置,以便以后再來
query: { redirect: to.fullPath },
}
}
})
TypeScript
可以通過擴(kuò)展 RouteMeta 接口來輸入 meta 字段:
// typings.d.ts or router.ts
import 'vue-router'
declare module 'vue-router' {
interface RouteMeta {
// 是可選的
isAdmin?: boolean
// 每個路由都必須聲明
requiresAuth: boolean
}
}
router.beforeResolve
router.beforeResolve(async to => {
if (to.meta.requiresCamera) {
try {
await askForCameraPermission()
} catch (error) {
if (error instanceof NotAllowedError) {
// ... 處理錯誤,然后取消導(dǎo)航
return false
} else {
// 意料之外的錯誤,取消導(dǎo)航并把錯誤傳給全局處理器
throw error
}
}
}
})
router.beforeResolve 是獲取數(shù)據(jù)或執(zhí)行任何其他操作(如果用戶無法進(jìn)入頁面時你希望避免執(zhí)行的操作)的理想位置。
全局后置鉤子
你也可以注冊全局后置鉤子,然而和守衛(wèi)不同的是,這些鉤子不會接受 next 函數(shù)也不會改變導(dǎo)航本身:
router.afterEach((to, from) => {
sendToAnalytics(to.fullPath)
})
它們對于分析、更改頁面標(biāo)題、聲明頁面等輔助功能以及許多其他事情都很有用。
它們也反映了 navigation failures 作為第三個參數(shù):
js
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)入路由時觸發(fā),不會在 params、query 或 hash 改變時觸發(fā)。例如,從 /users/2 進(jìn)入到 /users/3 或者從 /users/2#info 進(jìn)入到 /users/2#projects。它們只有在 從一個不同的 路由導(dǎo)航時,才會被觸發(fā)。
你也可以將一個函數(shù)數(shù)組傳遞給 beforeEnter,這在為不同的路由重用守衛(wèi)時很有用:
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],
},
]
請注意,你也可以通過使用``路徑 meta 字段和全局導(dǎo)航守衛(wèi)`來實(shí)現(xiàn)類似的行為。
組件內(nèi)的守衛(wèi)
最后,你可以在路由組件內(nèi)直接定義路由導(dǎo)航守衛(wèi)(傳遞給路由配置的)
可用的配置 API
你可以為路由組件添加以下配置:
beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave
const UserDetails = {
template: `...`,
beforeRouteEnter(to, from) {
// 在渲染該組件的對應(yīng)路由被驗(yàn)證前調(diào)用
// 不能獲取組件實(shí)例 `this` !
// 因?yàn)楫?dāng)守衛(wèi)執(zhí)行時,組件實(shí)例還沒被創(chuàng)建!
},
beforeRouteUpdate(to, from) {
// 在當(dāng)前路由改變,但是該組件被復(fù)用時調(diào)用
// 舉例來說,對于一個帶有動態(tài)參數(shù)的路徑 `/users/:id`,在 `/users/1` 和 `/users/2` 之間跳轉(zhuǎn)的時候,
// 由于會渲染同樣的 `UserDetails` 組件,因此組件實(shí)例會被復(fù)用。而這個鉤子就會在這個情況下被調(diào)用。
// 因?yàn)樵谶@種情況發(fā)生的時候,組件已經(jīng)掛載好了,導(dǎo)航守衛(wèi)可以訪問組件實(shí)例 `this`
},
beforeRouteLeave(to, from) {
// 在導(dǎo)航離開渲染該組件的對應(yīng)路由時調(diào)用
// 與 `beforeRouteUpdate` 一樣,它可以訪問組件實(shí)例 `this`
},
}
beforeRouteEnter 守衛(wèi) 不能 訪問 this,因?yàn)槭匦l(wèi)在導(dǎo)航確認(rèn)前被調(diào)用,因此即將登場的新組件還沒被創(chuàng)建。
不過,你可以通過傳一個回調(diào)給 next 來訪問組件實(shí)例。在導(dǎo)航被確認(rèn)的時候執(zhí)行回調(diào),并且把組件實(shí)例作為回調(diào)方法的參數(shù):
beforeRouteEnter (to, from, next) {
next(vm => {
// 通過 `vm` 訪問組件實(shí)例
})
}
注意 beforeRouteEnter 是支持給 next 傳遞回調(diào)的唯一守衛(wèi)。對于 beforeRouteUpdate 和 beforeRouteLeave 來說,this 已經(jīng)可用了,所以不支持 傳遞回調(diào),因?yàn)闆]有必要了:
beforeRouteUpdate (to, from) {
// just use `this`
this.name = to.params.name
}
這個 離開守衛(wèi) 通常用來預(yù)防用戶在還未保存修改前突然離開。該導(dǎo)航可以通過返回 false 來取消。
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ù)來編寫組件,你可以通過 onBeforeRouteUpdate 和 onBeforeRouteLeave 分別添加 update 和 leave 守衛(wèi)。 請參考組合 API 部分以獲得更多細(xì)節(jié)。
完整的導(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í)例會作為回調(diào)函數(shù)的參數(shù)傳入。
到此這篇關(guān)于Vue中的路由導(dǎo)航守衛(wèi)導(dǎo)航解析流程的文章就介紹到這了,更多相關(guān)Vue路由導(dǎo)航守衛(wèi)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
手寫實(shí)現(xiàn)vue2下拉菜單dropdown組件實(shí)例
這篇文章主要為大家介紹了手寫vue2下拉菜單dropdown組件實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
vue3?錨點(diǎn)定位的多種實(shí)現(xiàn)方式
這篇文章主要介紹了vue3?多種方法的錨點(diǎn)定位,使用?Vue?Router?導(dǎo)航守衛(wèi)可以簡化導(dǎo)航邏輯、統(tǒng)一管理導(dǎo)航邏輯和進(jìn)行權(quán)限控制,但需要學(xué)習(xí)和理解相關(guān)概念,并且需要手動編寫和管理導(dǎo)航守衛(wèi)的邏輯,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07
Vue3新屬性之css中使用v-bind的方法(v-bind?in?css)
這篇文章主要介紹了Vue3新屬性css中使用v-bind(v-bind?in?css)的方法,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-01-01
Vue3 構(gòu)建 Web Components使用詳解
這篇文章主要為大家介紹了Vue3 構(gòu)建 Web Components使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
vue實(shí)現(xiàn)導(dǎo)航標(biāo)題欄隨頁面滾動漸隱漸顯效果
這篇文章主要介紹了vue實(shí)現(xiàn)導(dǎo)航標(biāo)題欄隨頁面滾動漸隱漸顯效果,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03
vue 數(shù)組和對象不能直接賦值情況和解決方法(推薦)
這篇文章主要介紹了vue 數(shù)組和對象不能直接賦值情況和解決方法,需要的朋友可以參考下2017-10-10
vue 基于abstract 路由模式 實(shí)現(xiàn)頁面內(nèi)嵌的示例代碼
這篇文章主要介紹了vue 基于abstract 路由模式 實(shí)現(xiàn)頁面內(nèi)嵌的示例代碼,幫助大家更好的理解和使用vue框架,感興趣的朋友可以了解下2020-12-12

