vue-router 源码:路由的安装与初始化
使用
日常我們使用 vue-router 時:
在 template 中我們用 <router-link> 來做路由跳轉,用 <router-view> 來做路由跳轉后的展示。
<p><router-link to="/foo">Go to Foo</router-link><router-link to="/bar">Go to Bar</router-link> </p> <router-view></router-view> 復制代碼在 js 中,先用 Vue.use 來安裝 vue-router,然后 new 一個 VueRouter 實例,最后將實例注入到 Vue 實例中。
Vue.use(VueRouter)const router = new VueRouter({routes })const app = new Vue({router }).$mount('#app') 復制代碼這樣就實現(xiàn)了一個路由系統(tǒng)。我把這個過程稱為路由的安裝與初始化。
那么這個過程里,vue-router 做了什么事情呢?接下來就來一探究竟。
對了,我選的是 vue-router v2.0.1 的代碼來閱讀。
Flow
在閱讀 vue-router 的源碼前,我們需要了解一下 Flow。
Flow 是 Facebook 的 JavaScript 靜態(tài)類型檢查工具,語法跟 TypeScript 有點類似。
源碼里就用了 Flow 來做靜態(tài)類型檢查。Vue-router 在 Flow 中的自定義類型存放在項目里的 flow 目錄下。
想必你會問,為什么不用 TypeScript 而是用 Flow 呢?這個作者的回答是最權威的,戳 這里 了解一下吧。
猜想
在閱讀之前,我們先來簡單猜想一下,路由安裝與初始化會做哪些事情。
install
正式開始閱讀代碼了,來驗證上面的猜想是否正確吧。
在 src/index.js 文件中,有一段這樣的代碼:
import { install } from './install'// ...VueRouter.install = installif (inBrowser && window.Vue) {window.Vue.use(VueRouter) } 復制代碼我們知道調用了 Vue.use 就會默認去調用 install 方法,所以跳轉到 src/install.js 文件中。
找到這段代碼:
Vue.mixin({beforeCreate () {if (this.$options.router) {this._router = this.$options.routerthis._router.init(this)Vue.util.defineReactive(this, '_route', this._router.history.current)}} }) 復制代碼這段代碼,就是將 VueRouter 對象實例注入 Vue 實例中,賦值給屬性 _router,同時創(chuàng)造了另一個屬性 _route。
注意,這里還會調用 init 方法,接下來會閱讀到。
下劃線表示私有屬性,如果需要給外部使用,則需要暴露一個方法或外部屬性出來:
Object.defineProperty(Vue.prototype, '$router', {get () { return this.$root._router } })Object.defineProperty(Vue.prototype, '$route', {get () { return this.$root._route } }) 復制代碼這樣子,就可以在 Vue 的組件里 this.$router 或 this.$route 的調用。
最后注冊了兩個全局組件,<router-link> 和 <router-view>。
Vue.component('router-view', View) Vue.component('router-link', Link) 復制代碼這里有一個需要注意的點,Vue.use 會調用 install 方法,即以上的代碼都會執(zhí)行,但是 Vue 的 beforeCreate 鉤子是在 new Vue 的時候才會執(zhí)行。
意思就是 new VueRouter 會在 beforeCreate 之前執(zhí)行。即會先執(zhí)行 VueRouter 的 constructor 構造函數(shù)。
constructor
來看看 VueRouter 的 constructor 構造函數(shù)做了哪些事情吧。constructor 的代碼不多,主要是初始化一些屬性。
constructor (options: RouterOptions = {}) {this.app = nullthis.options = optionsthis.beforeHooks = []this.afterHooks = []this.match = createMatcher(options.routes || [])let mode = options.mode || 'hash'this.fallback = mode === 'history' && !supportsHistoryif (this.fallback) {mode = 'hash'}if (!inBrowser) {mode = 'abstract'}this.mode = mode } 復制代碼里面的 createMatcher 先跳過,這又是另一大塊,暫時不管。
可以看到后面的代碼就是在設置 this.mode,即路由模式。
默認是 hash 模式,如果設置了 history 還得判斷支持該種模式不,不支持則下降為默認的模式。如果代碼不是運行在瀏覽器而是在 node 端,則設置為 abstract 模式,這個也先跳過哈哈哈。
在 beforeCreate 會調用 VueRouter 的 init 方法,來看看里面做了什么初始化工作吧。
init
init 方法是這么被調用的:
this._router.init(this) 復制代碼這里的 this 指向的是 Vue 實例。
再來看看 init 里面的實現(xiàn)(過濾掉部分代碼):
init (app: any /* Vue component instance */) {this.app = appconst { mode, options, fallback } = thisswitch (mode) {case 'history':this.history = new HTML5History(this, options.base)breakcase 'hash':this.history = new HashHistory(this, options.base, fallback)breakcase 'abstract':this.history = new AbstractHistory(this)breakdefault:assert(false, `invalid mode: ${mode}`)}this.history.listen(route => {this.app._route = route}) } 復制代碼init 實現(xiàn)的是,通過剛剛 constructor 設置的 mode 來生成一個新的屬性 this.history。
this.history 是根據(jù)不同的 mode 來 new 出不同的對象實例。像 history 模式就用 HTML5History,hash 模式就用 HashHistory。
對象里面的實現(xiàn)待以后再深入吧。現(xiàn)在我們只要知道有這么一個新屬性 this.history 即可。
this.history 通過調用 listen 方法,將更新 _route 的操作保存起來,在以后更新路由的時候,會執(zhí)行該操作來更新 _route。講了跟沒講一樣哈哈,沒關系,現(xiàn)在只要知道有這代碼存在就行了。
回顧
VueRouter 的路由安裝與初始化做了哪些事情。按順序來:
(一)
調用 install 方法,注冊兩個組件,<router-link> 和 <router-view>。
(二)
new 了 VueRouter 實例,調用 contructor 構造函數(shù),初始化了一些屬性,其中包括 mode 屬性,用來保存路由模式。
(三)
new 了 Vue 實例,調用其 beforeCreate,將 VueRouter 對象實例注入 Vue 實例中,并調用 install 方法。install 方法則在根據(jù)不同的路由模式新增一個 history 屬性。history 屬性保存的對象里面又是一片天地,待續(xù)。
總結
以上是生活随笔為你收集整理的vue-router 源码:路由的安装与初始化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 移动端数据统计,精细化运营的永动机
- 下一篇: 开源社区ITGeek介绍