溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Vue-Router深入源碼分析:index.js

發(fā)布時間:2020-07-02 08:54:21 來源:網(wǎng)絡(luò) 閱讀:10003 作者:MDove 欄目:web開發(fā)

前言

雖然最近需求著實不少,但是感覺自己學(xué)習(xí)勁頭還是蠻足的,并沒有被需求壓垮。今天,帶來Vue-Router源碼解析系列的第二篇文章:index.js。

正文

vue-router類里面都做了什么?

index.js是vue-router這個類的主構(gòu)造函數(shù),所以內(nèi)容上算是比較關(guān)鍵的:

Vue-Router深入源碼分析:index.jscdn.xitu.io/2018/9/9/165bd0e1aff64406?w=1314&h=1158&f=png&s=225796">
從圖片中我們可以看出來,這是一個ES6聲明類的方法,vue-router源碼中類的聲明都是使用類ES的語法,constructor (options: RouterOptions = {}),在vue-router中使用了flow.js做了類型的檢查,

什么是flow.js?flow.js怎么使用呢?因為篇幅原因,這里就暫時先不做涉及。各位小伙伴,可以參看官網(wǎng):https://flow.org/en/docs/types/

解析:constructor

首先我們來看一下constructor內(nèi)的代碼,

constructor (options: RouterOptions = {}) {
    this.app = null
    this.apps = []
    this.options = options
    this.beforeHooks = []
    this.resolveHooks = []
    this.afterHooks = []
    this.matcher = createMatcher(options.routes || [], this)
    //默認為hash錨點
    let mode = options.mode || 'hash'
      //當(dāng)然使用的是history模式 h6的pushState的方式來實現(xiàn)路由跳轉(zhuǎn)的,對options設(shè)置fallback屬性為true時會回退到hash模式
      // 是否支持回退
    this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
    if (this.fallback) {
      mode = 'hash'
    }
    if (!inBrowser) {
      mode = 'abstract'
    }
    //沒有fallback的話選擇錨點模式,node環(huán)境選擇abstract模式
    this.mode = mode

    switch (mode) {
      case 'history':
        this.history = new HTML5History(this, options.base)
        break
      case 'hash':
        this.history = new HashHistory(this, options.base, this.fallback)
        break
      case 'abstract':
        this.history = new AbstractHistory(this, options.base)
        break
      default:
        if (process.env.NODE_ENV !== 'production') {
          assert(false, `invalid mode: ${mode}`)
        }
    }
  }

我們聲明一個vue-router實例的時候是怎么做的?

let router = new Router({
    base : '/',
    mode : 'history',
    routes : [{
        component : xxx,
        path : xxx
    },xxx]
})
constructor (options: RouterOptions = {}) 
options就是我們剛才上面的一個對象,里面有base、mode、routes等屬性

這時候我們知道options是個什么東西了,我們來看看內(nèi)部對options進行了哪些處理。

首先我們說一下vue-router最核心的內(nèi)容之一:

解析:mode

我們知到vue-router的路由有兩種方式,一種是#錨點性的,第二種是和正常路徑一樣的,可是vue構(gòu)建的應(yīng)用是一個但頁面應(yīng)用如何讓他像正常的多頁面應(yīng)用一樣,是在不停的改變路徑呢?
這里面就使用了html5的history的pushState與replaceState(讓頁面看起來無刷新的改變路徑),具體內(nèi)容大家可以看一下官網(wǎng)文檔和大神張鑫旭的博客(https://www.zhangxinxu.com/wordpress/2013/06/html5-history-api-pushstate-replacestate-ajax/)

在vue-router源碼中有一個工具類專門做了這個事情:
Vue-Router深入源碼分析:index.js
我們來看一下vue-router是如何匹配mode的吧:

// vue-router默認使用hash模式
let mode = options.mode || 'hash';
this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
// 如果選擇了history但是pushState方法并不能使用并且設(shè)置了
// 在當(dāng)瀏覽器不支持 history.pushState 控制路由是否應(yīng)該回退到 hash 模式的情況下。
// (options.fallback默認就是true)
// 如果發(fā)現(xiàn)需要回退了,就回到hash錨點模式
    if (this.fallback) {
      mode = 'hash'
    }
// 不在瀏覽器環(huán)境就選擇abstract模式(在node環(huán)境)
    if (!inBrowser) {
      mode = 'abstract'
    }
    this.mode = mode
// 根據(jù)三種情況是成不同的路由轉(zhuǎn)換實例。
// 如果沒有mode不是這三種情況就報錯。
// HTML5History、HTML5History、HTML5History三個類都是繼承與一個base類
// 里面有這三種模式對于路徑轉(zhuǎn)換時做的事情進行了一定的封裝。
    switch (mode) {
      case 'history':
        this.history = new HTML5History(this, options.base)
        break
      case 'hash':
        this.history = new HTML5History(this, options.base, this.fallback)
        break
      case 'abstract':
        this.history = new HTML5History(this, options.base)
        break
      default:
        if (process.env.NODE_ENV !== 'production') {
          assert(false, `invalid mode: ${mode}`)
        }
    }

到這里大家應(yīng)該對我們寫的mode模式有一點的了解了吧。

解析:init

下面說一下init方法,上一章我們講了在根節(jié)點的beforeCreate生命周期鉤子中,使用了init方法,如果忘記了可以翻看上一篇install方法的學(xué)習(xí)來回歸一下

所以app就是根組件,init在執(zhí)行前要判斷一下,vue-router是不是被vue成功use了,因為成功use之后,會把install方法的installed屬性設(shè)置為true:

Vue-Router深入源碼分析:index.js

init (app: any /* Vue component instance */) {
    process.env.NODE_ENV !== 'production' && assert(
      install.installed,
      `not installed. Make sure to call \`Vue.use(VueRouter)\` ` +
      `before creating root instance.`
    )
    this.apps.push(app)
    // main app already initialized. (根組件已經(jīng)被初始化)
    if (this.app) {
      return
    }

    this.app = app

    const history = this.history
    // 當(dāng)我們的根組件完成了對vue-router的init的時候我們就要完成第一次路由的跳轉(zhuǎn)了
    // 當(dāng)我們的項目啟動的時候肯定會有一個路徑,這個路徑是什么不重要
    // 我們在第一次進入這個路徑的時候,會進行vue-router的初始化
    // 初始化之后要開始展示對應(yīng)的組件,可是我們vue-router那些popState的事件肯定沒有綁定,不會觸發(fā)啊,
    // 怎么辦?? 那就手動觸發(fā)一下這個事情,
    // 第一次進入肯定沒有對應(yīng)的事件,不會完成跳轉(zhuǎn)時該做的事情。
    if (history instanceof HTML5History) {
      history.transitionTo(history.getCurrentLocation())
    } else if (history instanceof HashHistory) {
      const setupHashListener = () => {
        history.setupListeners()
      }
      history.transitionTo(
        history.getCurrentLocation(),
        setupHashListener,
        setupHashListener
      )
    }
    // 針對設(shè)置的不同mode(模式)
    // 將mode的時候,每種模式的history實例來源于三個不同的類,所以instanceof足夠判斷是哪種模式。
到現(xiàn)在,我們上一章的init函數(shù)已經(jīng)串聯(lián)起來了,不知道大家感覺怎么樣?我感覺舒服了很多了。

路由守衛(wèi)

用過vue-router的同學(xué)們都知道路由守衛(wèi)的概念,這是在路由跳轉(zhuǎn)的前后等三個地方設(shè)置了不同的鉤子,幫助我們在進入離開路由前做一些事情。這個鉤子是怎么做的呢?

Vue-Router深入源碼分析:index.js
聲明了三個數(shù)組,存放每個周期內(nèi)的鉤子上綁定的函數(shù)。

vue-router全局級別的beforeEach、beforeResolve、afterEach做了什么?

Vue-Router深入源碼分析:index.js
就這么兩行代碼你敢信??我也很糾結(jié)你就干了這么點事情。
執(zhí)行一下registerHook函數(shù),從字面意思一看就是注冊鉤子,

怎么注冊的呢?
function registerHook (list: Array<any>, fn: Function): Function {
  list.push(fn)
  // 返回值是一個function
  return () => {
    const i = list.indexOf(fn)
    if (i > -1) list.splice(i, 1)
  }
}

接收一個生命周期的鉤子數(shù)組,將我們要執(zhí)行的函數(shù)傳到數(shù)組內(nèi)就可以完成注冊了,我還沒看到這三個數(shù)組的內(nèi)容,但是直覺告訴我很有可能就是,觀察者模式(以后就探索去)。注冊到這應(yīng)該就OK了,

為什么還有個返回值呢?
返回值的內(nèi)容一看就是要清楚鉤子內(nèi)的函數(shù)呀,我們調(diào)用這個registerHook函數(shù)后,可以得到注冊函數(shù)的清除函數(shù),清除的是鉤子數(shù)組中對應(yīng)的函數(shù),還有這么一手,牛的一匹。(讓代碼教你如何熟練使用閉包~)

vue-router的編程式導(dǎo)航是怎么做的?

Vue-Router深入源碼分析:index.js
push方法與replace、go方法調(diào)用對應(yīng)路由轉(zhuǎn)換實例的對應(yīng)方法,因為不同模式大家方法肯定都不一樣,
back與forward都是go方法傳入特殊參數(shù),所以看到這里我們發(fā)現(xiàn)history這個實例的內(nèi)容很關(guān)鍵。


清一清嗓

到了這里我們vue-router的主線流程我們已經(jīng)進行了一個梳理,不知道大家對這一塊內(nèi)容感覺滿意嗎? 不滿意就請不要郵寄刀片哈。

所以到現(xiàn)在我們簡單進行一下總結(jié)

1:mode是設(shè)置模式的,有hash、history、abstract三種模式、每個模式會導(dǎo)致vue-router實例的history不同,來自三個不同的類、每個類又繼承于總的base類。
2:init方法會初始化整個組件、并且在vue-router的實例中設(shè)置了app屬性存放根組件(這個確實很有用)、手動的完成初始化后的第一次路由跳轉(zhuǎn)。
3:beforeEach、beforeREsolve、afterEach三個全局的鉤子都有對應(yīng)的鉤子函數(shù)數(shù)組,存放每個周期鉤子內(nèi)要做的事情、使用registerHook方法來完成鉤子函數(shù)的注冊,registerHook也可以清除鉤子內(nèi)對應(yīng)的函數(shù)。
4:push、replace、go等方法都是使用history方法內(nèi)的對應(yīng)方法。

總結(jié)完畢 我們學(xué)到了什么? history真重要,我要好好看看他內(nèi)部的實現(xiàn)

真的沒出息的總結(jié)。

vue-router類中還有一部分對options.routes的處理

options.routes 就是 [{path : 'xxx',components : 'xxx'}] 這個數(shù)組

生成一個根據(jù)options.routes的一份比對程序,完成程序的比對。

下一期的內(nèi)容要在繼續(xù)學(xué)習(xí)index.js和開荒history中進行一個抉擇,具體是什么內(nèi)容大家可以積極留言,給個方向呀。

結(jié)束語

每一個前端er(boy and girl) 你們都不是一個人在戰(zhàn)斗。

安排!?。?!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI