您好,登錄后才能下訂單哦!
這篇文章主要介紹“Vue中的路由導(dǎo)航及守衛(wèi)導(dǎo)航源碼分析”的相關(guān)知識(shí),小編通過實(shí)際案例向大家展示操作過程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“Vue中的路由導(dǎo)航及守衛(wèi)導(dǎo)航源碼分析”文章能幫助大家解決問題。
你可以使用 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)航正要離開的路由
可以返回的值如下:
false
: 取消當(dāng)前的導(dǎo)航。如果瀏覽器的 URL 改變了(可能是用戶手動(dòng)或者瀏覽器后退按鈕),那么 URL 地址會(huì)重置到 from
路由對(duì)應(yīng)的地址。
一個(gè)路由地址: 通過一個(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) => { if ( // 檢查用戶是否已登錄 !isAuthenticated && // ?? 避免無限重定向 to.name !== 'Login' ) { // 將用戶重定向到登錄頁面 return { name: 'Login' } } })
如果遇到了意料之外的情況,可能會(huì)拋出一個(gè) Error
。這會(huì)取消導(dǎo)航并且調(diào)用 router.onError()
注冊(cè)過的回調(diào)。
如果什么都沒有,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è)常見的錯(cuò)誤來源,可以通過 RFC 來消除錯(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() })
你可以用 router.beforeResolve
注冊(cè)一個(gè)全局守衛(wèi)。這和 router.beforeEach
類似,因?yàn)樗?每次導(dǎo)航時(shí)都會(huì)觸發(fā),但是確保在導(dǎo)航被確認(rèn)之前,同時(shí)在所有組件內(nèi)守衛(wèi)和異步路由組件被解析之后,解析守衛(wèi)就被正確調(diào)用。這里有一個(gè)例子,確保用戶可以訪問自定義 meta屬性 requiresCamera
的路由:
路由元信息
有時(shí),你可能希望將任意信息附加到路由上,如過渡名稱、誰可以訪問路由等。這些事情可以通過接收屬性對(duì)象的meta
屬性來實(shí)現(xiàn),并且它可以在路由地址和導(dǎo)航守衛(wèi)上都被訪問到。定義路由的時(shí)候你可以這樣配置 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 } } ] } ]
那么如何訪問這個(gè) meta
字段呢?
首先,我們稱呼 routes
配置中的每個(gè)路由對(duì)象為 路由記錄。路由記錄可以是嵌套的,因此,當(dāng)一個(gè)路由匹配成功后,它可能匹配多個(gè)路由記錄。
例如,根據(jù)上面的路由配置,/posts/new
這個(gè) URL 將會(huì)匹配父路由記錄 (path: '/posts'
) 以及子路由記錄 (path: 'new'
)。
一個(gè)路由匹配到的所有路由記錄會(huì)暴露為 $route
對(duì)象(還有在導(dǎo)航守衛(wèi)中的路由對(duì)象)的$route.matched
數(shù)組。我們需要遍歷這個(gè)數(shù)組來檢查路由記錄中的 meta
字段,但是 Vue Router 還為你提供了一個(gè) $route.meta
方法,它是一個(gè)非遞歸合并所有 meta
字段的(從父字段到子字段)的方法。這意味著你可以簡(jiǎn)單地寫
router.beforeEach((to, from) => { // 而不是去檢查每條路由記錄 // to.matched.some(record => record.meta.requiresAuth) if (to.meta.requiresAuth && !auth.isLoggedIn()) { // 此路由需要授權(quán),請(qǐng)檢查是否已登錄 // 如果沒有,則重定向到登錄頁面 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 // 每個(gè)路由都必須聲明 requiresAuth: boolean } }
router.beforeResolve
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í)行任何其他操作(如果用戶無法進(jìn)入頁面時(shí)你希望避免執(zhí)行的操作)的理想位置。
你也可以注冊(cè)全局后置鉤子,然而和守衛(wèi)不同的是,這些鉤子不會(huì)接受 next
函數(shù)也不會(huì)改變導(dǎo)航本身:
router.afterEach((to, from) => { sendToAnalytics(to.fullPath) })
它們對(duì)于分析、更改頁面標(biāo)題、聲明頁面等輔助功能以及許多其他事情都很有用。
它們也反映了 navigation failures 作為第三個(gè)參數(shù):
js
router.afterEach((to, from, failure) => { if (!failure) sendToAnalytics(to.fullPath) })
了解更多關(guān)于 navigation failures 的信息在它的指南中。
你可以直接在路由配置上定義 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)注意,你也可以通過使用``路徑 meta 字段和全局導(dǎo)航守衛(wèi)`來實(shí)現(xiàn)類似的行為。
最后,你可以在路由組件內(nèi)直接定義路由導(dǎo)航守衛(wèi)(傳遞給路由配置的)
你可以為路由組件添加以下配置:
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í)例還沒被創(chuàng)建! }, beforeRouteUpdate(to, from) { // 在當(dāng)前路由改變,但是該組件被復(fù)用時(shí)調(diào)用 // 舉例來說,對(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)可以訪問組件實(shí)例 `this` }, beforeRouteLeave(to, from) { // 在導(dǎo)航離開渲染該組件的對(duì)應(yīng)路由時(shí)調(diào)用 // 與 `beforeRouteUpdate` 一樣,它可以訪問組件實(shí)例 `this` }, }
beforeRouteEnter
守衛(wèi) 不能 訪問 this
,因?yàn)槭匦l(wèi)在導(dǎo)航確認(rèn)前被調(diào)用,因此即將登場(chǎng)的新組件還沒被創(chuàng)建。
不過,你可以通過傳一個(gè)回調(diào)給 next
來訪問組件實(shí)例。在導(dǎo)航被確認(rèn)的時(shí)候執(zhí)行回調(diào),并且把組件實(shí)例作為回調(diào)方法的參數(shù):
beforeRouteEnter (to, from, next) { next(vm => { // 通過 `vm` 訪問組件實(shí)例 }) }
注意 beforeRouteEnter
是支持給 next
傳遞回調(diào)的唯一守衛(wèi)。對(duì)于 beforeRouteUpdate
和 beforeRouteLeave
來說,this
已經(jīng)可用了,所以不支持 傳遞回調(diào),因?yàn)闆]有必要了:
beforeRouteUpdate (to, from) { // just use `this` this.name = to.params.name }
這個(gè) 離開守衛(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 和 setup
函數(shù)來編寫組件,你可以通過 onBeforeRouteUpdate
和 onBeforeRouteLeave
分別添加 update 和 leave 守衛(wèi)。
導(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ù)傳入。
關(guān)于“Vue中的路由導(dǎo)航及守衛(wèi)導(dǎo)航源碼分析”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。