路由響應過程:
-
瀏覽器發出請求
-
服務器監聽到num端口(或443)有請求過來,並解析url路徑
-
-
瀏覽器根據數據包的 Content-Type 來決定如何解析數據
let router = new Router({ mode: 'history|hash|abstract', routes: [ { // 默認頁 path: '*', redirect: to => { return '/' }, meta: { status: *** } }, { path: '/', name: '****', component: ****, meta: { status: *** } }, ], beforeEnter: (to, from, next) => {}), scrollBehavior: fun() })
在使用vueRouter的時候,我們會在項目中使用Vue.use(Router)安裝,它會加載VueRouter中的 install 方法使得所有組件都可以使用router的實例(this.$router/this.$route) 在install的時候實際上完成一些初始化的工作
(源碼install.js)
1)在vue.prototype上注冊一個$router/$route的實例
Vue.mixin({ beforeCreate () { if (isDef(this.$options.router)) { this._routerRoot = this this._router = this.$options.router Object.defineProperty(Vue.prototype, '$router', { get () { return this._routerRoot._router } }) Object.defineProperty(Vue.prototype, '$route', { get () { return this._routerRoot._route } })
2)全局注冊RouterView, RouterLink這兩個組件
Vue.component('RouterView', View) Vue.component('RouterLink', Link)
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}`) } }
(源碼hash.js)
// supportsPushState用於用於判斷瀏覽器是否支持h5 window.addEventListener(supportsPushState ? 'popstate' : 'hashchange', () => { const current = this.current if (!ensureSlash()) { return } this.transitionTo(getHash(), route => { if (supportsScroll) { handleScroll(this.router, route, current, true) } if (!supportsPushState) { // 如果不支持pushState 就使用window.location.replace(getUrl(path)) replaceHash(route.fullPath) } }) })
b)history
在h5之后,得到瀏覽器的支持就可以使用history模式了,在history模式下路由的改變會觸發popstate事件,因此history模式原理也是對popstate事件的監聽,然后做出相應操作。
(源碼html5.js)
window.addEventListener('popstate', e => { const current = this.current // Avoiding first `popstate` event dispatched in some browsers but first // history route not updated since async guard at the same time. const location = getLocation(this.base) if (this.current === START && location === initLocation) { return } this.transitionTo(location, route => { if (supportsScroll) { handleScroll(router, route, current, true) } }) })
c)abstract
abstract 模式是用於原生開發(非瀏覽器typeof window=== 'undefined')的情況下,這就意味着不能使用window對象;另外由於在原生app中,頁面儲存一個數組棧中,它記錄着頁面的的訪問記錄,因此在js中最簡單的就是定義數組進行模仿。
(源碼abstrack.js)
constructor (router: Router, base: ?string) { super(router, base) this.stack = [] // 定義一個stack數組,用於存儲路由 this.index = -1 // 記錄每個路由在stack中的索引 }
3)history.pushState()/replaceState() 會修改url ,不會使得頁面更新,也不會觸發window.popstate事件。
在history模式下使得頁面url與代碼中路由地址相同
4)history.back() /go(),window.location.hash 都會使得頁面改變,觸發window.popstate事件。
對此事件進行攔截處理一些事情(參數接收,頁面位置記錄)
window.addEventListener(supportsPushState ? 'popstate' : 'hashchange', () => { const current = this.current if (!ensureSlash()) { return } this.transitionTo(getHash(), route => { if (supportsScroll) { handleScroll(this.router, route, current, true) } if (!supportsPushState) { replaceHash(route.fullPath) } }) })
5)使用導航的時候其實都是使用的 this.history.push()/ go() /replace() 進行頁面切換。
push (location: RawLocation, onComplete?: Function, onAbort?: Function) { this.history.push(location, onComplete, onAbort) } replace (location: RawLocation, onComplete?: Function, onAbort?: Function) { this.history.replace(location, onComplete, onAbort) } go (n: number) { this.history.go(n) } back () { this.go(-1) }
forward () { this.go(1) }
https://segmentfault.com/a/1190000015123061