vue-router 結合源碼分析原理


路由響應過程:
  1. 瀏覽器發出請求

  2. 服務器監聽到num端口(或443)有請求過來,並解析url路徑

  3. 根據服務器的路由配置,返回相應信息(可以是 html 字串,也可以是 json 數據,圖片等)

  4. 瀏覽器根據數據包的 Content-Type 來決定如何解析數據

一般的vueRouter的代碼模式是這樣的:
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()
})

可以看到的是使用Router這個類進行實例化【new Router(options)】

在使用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)
 
分為三種模式: hash, history(h5), abstract

(源碼index.js)

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}`)
    }
}
 
三種模式的簡單描述: 

a)hash

常見形式:http(https)://xxxxxx/#/xxxx

在h5之前瀏覽器不支持popState(window),pushState(history)和 replaceState(history)這幾個事件,因此是通過hash值來改變路由,后面 hash 值的變化,並不會導致瀏覽器向服務器發出請求,瀏覽器不發出請求,也就不會刷新頁面。另外每次 hash 值的變化,還會觸發hashchange 這個事件,通過這個事件我們就可以知道 hash 值發生了哪些變化。然后我們便可以監聽hashchange來實現更新頁面部分內容的操作:

(源碼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中的索引
}
 
關於popstate,pushState, replaceState: 

1)window.popstate,history.pushState,history.replaceState是h5中新增的幾個方法。

2)hash模式情況下路由的該變會觸發window.hashtate,history模式下會觸發window.popstate事件。

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

https://zhuanlan.zhihu.com/p/24574970

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM