單頁Web應用(single page web application,SPA),就是隻有一張Web頁面的應用,是加載單個HTML 頁面並在用戶與應用程序交互時動態更新該頁面的Web應用程序。簡單來講就是用戶只須要加載一次頁面就能夠再也不請求,當點擊其餘子頁面時只會有相應的URL改變而不會從新加載。
咱們能夠將實現路由的過程分爲兩部分:javascript
如今主流有2種實現方案:html
接下來咱們一步一步看Vue-router如何實現的vue
請各位同窗翻到 src/index.js 第18行html5
export default class VueRouter { static install: () => void; static version: string; app: any; apps: Array<any>; ready: boolean; readyCbs: Array<Function>; options: RouterOptions; mode: string; history: HashHistory | HTML5History | AbstractHistory; matcher: Matcher; fallback: boolean; beforeHooks: Array<?NavigationGuard>; resolveHooks: Array<?NavigationGuard>; afterHooks: Array<?AfterNavigationHook>; constructor (options: RouterOptions = {}) { this.app = null this.apps = [] this.options = options this.beforeHooks = [] this.resolveHooks = [] this.afterHooks = [] this.matcher = createMatcher(options.routes || [], this) let mode = options.mode || 'hash' this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false if (this.fallback) { mode = 'hash' } if (!inBrowser) { mode = '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}`) } } }
構造器接收一個options參數java
默認mode爲 "hash",若是顯示傳入參數mode爲"history",則進行 是否支持的"history"的判斷web
this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
supportsPushState方法 裏面 判斷了 是否爲瀏覽器環境且當前瀏覽器版本支持historyvue-router
options.fallback用來控制路由,在設置了mode爲"history"可是當前瀏覽器環境不支持"history"的狀況下是否應該回調判斷,並從新設置mode爲"hash"。
設置fallback爲false本質上是爲了讓"router-link"在IE9上能夠完整的頁面刷新,若是是在hash模式下面不支持SSR,設置爲false,會讓那些在ie9服務端渲染的app更好用。(這段話沒具體應用過)數組
以後根據不一樣的mode,來執行不一樣的方案瀏覽器
構造器接收2個參數,router和base
router是在定義新路由的時候建立的對象
base若是沒有傳,則爲undefined,
"?"這是經過一個名爲flow的外部工具爲javascript加上強類型檢查的功能,不影響編譯和運行。直接無視就好。
調動History的構造方法,History爲 HTML5History,HashHistory,AbstractHistory的超類app
在History的構造方法中,
若是base爲undefined,查找是否有base的元素,有就賦值,沒有就'/'
以後
const expectScroll = router.options.scrollBehavior const supportsScroll = supportsPushState && expectScroll if (supportsScroll) { setupScroll() }
判斷路由參數,是否控制路由頁面滾動條行爲
監聽popstate事件,跳轉
獲取當前location的值以後,
進行路由的更新,好比當前的History對應哪一個路由
Html5History也添加了go,push,replace等方法用來路由跳轉,
先保存滾動條狀態,以後可使用history的自帶方法進行地址的改變
更多詳情請見MDN
未完待續
調動History的構造方法,History爲 HTML5History,HashHistory,AbstractHistory的超類
判斷當前hash地址
若是開頭不是/#,將當前location按照hash格式化
根據href獲取當前hash,若是沒有匹配到'#'返回空字符串。
初始化地址欄hash後
監聽popstate事件,替換路由,控制滾動條行爲
在registerHook將設置的守衛入棧
在每次跳轉的時候,遞歸守衛集合,將觸發的守衛進行解析和執行。
相對於上兩種方法,AbstractHistory看起來要簡單不少,這種模式是用於 Node.js 環境的,通常場景也就是在作測試的時候。可是在實際項目中其實還可使用的,利用這種特性仍是能夠很方便的作不少事情的。(我沒有用過)
由於不涉及和瀏覽器地址相關記錄關聯在一塊兒;總體流程依舊和 HashHistory 是同樣的,只是這裏經過數組來模擬瀏覽器歷史記錄堆棧信息。
更新歷史堆棧信息,更新當前所處位置
等等,除了不使用瀏覽器的history對象,其餘的和html5history模式差很少。
vue-router的源碼剖析到這裏就結束了,大概流程是這個樣子,得益於開發人員代碼的簡潔性及可讀性,咱們閱讀起來障礙仍是沒有那麼多,難度也沒有那麼大,總體邏輯不復雜,可是想要把不少不復雜的細節,整合到一塊兒,認真到細節,纔是程序設計的美學。