前言
這次真的是太久太久沒見了,夥伴們。 學習真的沒有鬆懈,只是季度末要衝一衝績效,一下子把時間都充值了。(ps:我看應該是我的腦子需要充值)
系列文章: Vue-Router原始碼分析之index.js
正文
為什麼要從API中看門道呢?
我們在使用vue-router的時候,其實就是按照API進行操作,原始碼其實就是API的另一種形式,在我的學習中,我發現一味的乾貨有時效果並不好,由其是深入思考性比較強的內容。因此本篇文章所以作為一個過渡章節,讓我們一起聊一聊承上啟下的內容,聊聊API:
我們的push有哪些情況?
在實際工作中,程式設計式導航要比<router-link>
標籤使用的多的多,複雜邏輯中巢狀路由跳轉都是要使用程式設計式導航。
// 字串
router.push('home')
// 物件
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: 123 }})
router.push({ path: `/user/${userId}` })
// 帶查詢引數,變成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
複製程式碼
在程式設計式路由中,我們可以接收一個物件、一個字串,物件的key有path、name、query、params等等,但是需要相互組合。
這麼多情況vue-router要對push這麼多情況做一個處理,我們push接收的引數的型別是什麼?用flow設定為Location、RawLocation
用flow.js設定的型別
declare type Location = {
_normalized?: boolean;
name?: string;
path?: string;
hash?: string;
query?: Dictionary<string>;
params?: Dictionary<string>;
append?: boolean;
replace?: boolean;
}
declare type RawLocation = string | Location
複製程式碼
翻譯過來就是未處理的、生的路徑。所以我們的push操作傳遞的是一個生冷的路徑,經過一次match的處理
原始碼內容:
const route = this.router.match(location, this.current)
複製程式碼
變成vue-router所需要的結構,route型別
declare type Route = {
path: string;
name: ?string;
hash: string;
query: Dictionary<string>;
params: Dictionary<string>;
fullPath: string;
matched: Array<RouteRecord>;
redirectedFrom?: string;
meta?: any;
}
複製程式碼
所以從API結合表層原始碼來看,我們在進行程式設計式導航時的過程,先將未處理的路徑(ps:vue-router對這種未處理路徑沒法直接進行跳轉等操作)處理成一個規範的route型別,然後進行具體的路由跳轉內容。
命名路由與檢視
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
複製程式碼
一個元件內可以有多個router-view元件,我接觸的幾個專案都是維護一個根結點的router-view。大部分都是這種結構
<template>
<div id="app">
<router-view/>
</div>
</template>
複製程式碼
我們一個路徑下可以展示多個路由檢視元件嗎?
答案當然是肯定的,官方的命名檢視的例子(jsfiddle.net/posva/22wgk…
可以看到,我們在同一個路徑下可以放置多個<router-link>
,這裡面的name與我們在構造vue-router時設定的components有對應,我們一般都是component : xxx
;對應一個元件。如何對應多個的呢?
原始碼內容:
components: route.components || { default: route.component },
複製程式碼
其實人家本身希望你用components,你要只用一個人家就給你包裝成一個map,key呢就是預設值的意思。
再看看我們的vue-router2開始的路由守衛
路由守衛有哪些呢?
-
1:全域性的beforeEach
-
2:單個路由配置的beforeEnter
-
3:全域性的afterEach
-
4:元件內的路由守衛:
beforeRouteEnter
beforeRouteUpdate (2.2 新增)
beforeRouteLeave
-
5:全域性的beforeResolve(vue-router2.5新增)
別的守衛還有嗎?這麼看來是沒有了。
全域性的beforeEnter、afterEach、beforeResolve為什麼會作用在每一個路由配置中呢?在index一章中,我們有三個存放守衛的陣列大家還有印象嗎?
beforeHooks: Array<?NavigationGuard>;
resolveHooks: Array<?NavigationGuard>;
afterHooks: Array<?AfterNavigationHook>;
複製程式碼
在router例項中儲存這些。所以我們在做路由跳轉的時候可以拿到這些守衛,其餘的守衛怎麼辦?只好在跳轉的時候具體情況具體執行,
守衛分為三個種類:
1:離開元件之前,2:進入元件(前後都有守衛) 3:更新元件之前
所以一個路由的跳轉一定伴隨著以下幾個步驟
-
1:跳轉我們稱之為transitionTo
-
2:那麼跳轉中進行一個confirmTransiton(跳轉前的準備)
-
3:跳轉結束時進行一個updateRoute的過程
上文提到的confirmTransition:先收集到所有的守衛,把前置守衛們(前置!!!)連線成一個陣列,跳轉前挨個執行,前置守衛執行完怎麼辦?都通過來,那就更新當前路由,更新完當前路由就執行一下後置守衛。 vue-router原始碼中:
核心的路由跳轉方法就是transitionTo: 具體內容是由confirmTransition與updateRoute實現的
其餘的一些這這那那的容錯,並不影響主流程。
總結一下
對於push、replace傳遞的未處理的路徑,進行處理成vue-router可以操作的路徑。
對於多個router-view,我們可以選擇children,components來實現。
路由守衛為什麼可以有全域性的,每個路由都可以用,因為在router例項上,我們每次跳轉都可以找到。
路由跳轉經歷了confirm確認與update更新兩步
結束語
中秋節剛剛過去,十一馬上就到了
沒有來得及給大家帶來祝福,希望大家十一的時候每次路由跳轉都不堵車!
每一個前端er(boy and girl) 你們都不是一個人在戰鬥