Vue-Router 原始碼學習之我們從API中看些門道

酸楚與甘甜發表於2018-09-26

前言

這次真的是太久太久沒見了,夥伴們。 學習真的沒有鬆懈,只是季度末要衝一衝績效,一下子把時間都充值了。(ps:我看應該是我的腦子需要充值)

系列文章: Vue-Router原始碼分析之index.js

Vue-Router原始碼分析之install方法

正文

為什麼要從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實現的

Vue-Router 原始碼學習之我們從API中看些門道

其餘的一些這這那那的容錯,並不影響主流程。

總結一下

對於push、replace傳遞的未處理的路徑,進行處理成vue-router可以操作的路徑。

對於多個router-view,我們可以選擇children,components來實現。

路由守衛為什麼可以有全域性的,每個路由都可以用,因為在router例項上,我們每次跳轉都可以找到。

路由跳轉經歷了confirm確認與update更新兩步

結束語

中秋節剛剛過去,十一馬上就到了

沒有來得及給大家帶來祝福,希望大家十一的時候每次路由跳轉都不堵車!

每一個前端er(boy and girl) 你們都不是一個人在戰鬥

我是一個應屆生,最近和朋友們維護了一個公眾號,內容是我們在從應屆生過渡到開發這一路所踩過的坑,已經我們一步步學習的記錄,如果感興趣的朋友可以關注一下,一同加油~

個人公眾號:IT面試填坑小分隊

相關文章