vue-router工做原理概述和問題分析

工做原理簡要流程圖

hashchange
-->
match route
-->
set vm._route
-->
<router-view> render()
-->
render matched component

問題

1. 爲何url變動時,router-view組件就能渲染出在routes中定義的與當前path匹配的組件?

  1. root vue實例上定義了一個響應式屬性 Vue.util.defineReactive(this, '_route', this._router.history.current)
  2. url變動時,會匹配到最新的route,而且會設置 this._routerRoot._route, 觸發setter
  3. router-view組件的render函數中有使用到 parent.$route, 也就是間接的觸發了this._routerRoot._routegetter。即進行了依賴蒐集。
  4. this._routerRoot._routesetter觸發時即會觸發router-view渲染watcher, 再次渲染, 而且此時拿到的路由組件也是最新的。

本質上利用了vue的響應式屬性,在route屬性變動和router-view視圖渲染之間創建關係。vue

route變動 => render從新渲染node

2. router-view render中使用到parent.$route爲何就會被this._routerRoot._route收集watcher

在掛載router-view組件時,會生成一個watcher, router-view的render函數中又觸發了_route的getter方法,那麼此watcher就被收集到_route的Dep中了。git

在_route的setter觸發時,天然執行了這個watcher, 組件從新render。github

在 vue的倉庫中vue/src/core/instance/lifecycle.jsmountComponent方法中,能看到掛載組件時是會生成一個watcher的:vue-router

export function mountComponent(
    vm: Component,
    el: ?Element,
    hydrating?: boolean
) {
    ...
    let updateComponent 
    updateComponent = () => {
        vm._update(vm._render(), hydrating)
    }
    
    new Watcher(vm, updateComponent, noop, {
        before() {
            ...
        }
    })
    ...
    return vm
}

3. this.$router, this.$route是怎麼掛載每一個vue組件上的?

Object.defineProperty(Vue.prototype, '$router', {
    get () { return this._routerRoot._router }
  })

  Object.defineProperty(Vue.prototype, '$route', {
    get () { return this._routerRoot._route }
  })

4.替換routes的寫法(這樣寫爲何有用)

// 替換現有router的routes
router.matcher = new VueRouter({
  routes: newRoutes
}).matcher

router.matcher是比較核心的一個屬性。對外提供兩個方法match(負責route匹配), addRoutes(動態添加路由)。框架

具體緣由:在作路徑切換transitionTo方法中,首先就會使用const route = this.router.match(location, this.current)來匹配route, 其實內部會使用matcher來作匹配。修改了matcher即新的routes生效。函數

match (
    raw: RawLocation,
    current?: Route,
    redirectedFrom?: Location
  ): Route {
    // 這裏使用matcher的match方法來作匹配
    return this.matcher.match(raw, current, redirectedFrom)
  }

對router.matcher屬性作修改,即新的routes就會替換老的routes, 其實就是replaceRoutes()的含義(可是官方沒有提供這個API)。oop

export type Matcher = {
  match: (raw: RawLocation, current?: Route, redirectedFrom?: Location) => Route;
  addRoutes: (routes: Array<RouteConfig>) => void;
};

5. router-view是什麼

<router-view> 組件是一個functional 組件,渲染路徑匹配到的視圖組件。<router-view> 渲染的組件還能夠內嵌本身的 <router-view>,根據嵌套路徑,渲染嵌套組件。this

<transition>
  <keep-alive>
    <router-view></router-view>
  </keep-alive>
</transition>

主要流程

  1. 初始化url

    1. 實例化Router, options做爲屬性,根據mode生成HTML5History實例,或HashHistory實例
    2. 數據劫持,this.router.route =》 this.history.current
    3. 數據劫持,this.history.current.route變化時,自動執行render。
    4. 當即執行一次路由跳轉(渲染路由組件)
  2. 路由監聽

    1. HashHistory監聽hashChange事件,HTML5History監聽popstate事件
  3. 路由變化處理

    1. 兩種方式觸發路由變化

      1. a標籤href方法(url先變化,會觸發兩個history監聽的hashChange或popstate事件,而後進入路由變化處理)
      2. 調用router的push, replace, go方法(先進入路由變化處理,而後修改url)
    2. 路由變化具體處理過程

      1. history.transitionTo(根據path匹配到相應的route, 而後調度執行hooks, 而後更新this.current屬性,觸發視圖更新)
      2. history.confirmTransition(調度執行上一個route和下一個route的相關生命週期hooks)
  4. router的輔助API

    1. push(先進行路由變化處理,在更新url,使用history.pushState)
    2. replace() 和push處理方式一致, 只是更新url使用history.replaceState
    3. go 使用history.go方法

參考連接

https://cnodejs.org/topic/58d... 這篇文章講大框架,和幾個重點問題 【推薦閱讀】

https://zhuanlan.zhihu.com/p/... 這篇文章講的也比較詳細,最好的是提供了本身造的vue-router的簡易版輪子,更方便你們理解,這個輪子很好。 【推薦閱讀輪子這一部分】

https://ustbhuangyi.github.io... 這個更加詳細,可是廢話太多,有點看不清重點,【隨緣閱讀】

相關文章
相關標籤/搜索