成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

簡(jiǎn)述vue-router實(shí)現(xiàn)原理

Cristalven / 351人閱讀

摘要:源碼解讀閱讀請(qǐng)關(guān)注下代碼注釋打個(gè)廣告哪位大佬教我下怎么排版啊,不會(huì)弄菜單二級(jí)導(dǎo)航撲通是什么首先,你會(huì)從源碼里面引入,然后再傳入?yún)?shù)實(shí)例化一個(gè)路由對(duì)象源碼基礎(chǔ)類源碼不選擇模式會(huì)默認(rèn)使用模式非瀏覽器環(huán)境默認(rèn)環(huán)境根據(jù)參數(shù)選擇三種模式的一種根據(jù)版

router源碼解讀

閱讀請(qǐng)關(guān)注下代碼注釋

打個(gè)廣告:哪位大佬教我下sf怎么排版啊,不會(huì)弄菜單二級(jí)導(dǎo)航(撲通.gif)

1. router是什么

首先,你會(huì)從源碼里面引入Router,然后再傳入?yún)?shù)實(shí)例化一個(gè)路由對(duì)象

// router/index.js
import Router from "vue-router"
new Router({...})
...

源碼基礎(chǔ)類:

// 源碼index.js
export default class VueRouter {
  ...
  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"   // 不選擇模式會(huì)默認(rèn)使用hash模式
    this.fallback = mode === "history" && !supportsPushState && options.fallback !== false
    if (this.fallback) {
      mode = "hash"
    }
    if (!inBrowser) {         // 非瀏覽器環(huán)境默認(rèn)nodejs環(huán)境
      mode = "abstract"
    }
    this.mode = mode

    switch (mode) { // 根據(jù)參數(shù)選擇三種模式的一種
      case "history":
        this.history = new HTML5History(this, options.base) // 根據(jù)HTML5版History的方法和屬性實(shí)現(xiàn)的模式
        break
      case "hash":
        this.history = new HashHistory(this, options.base, this.fallback) // 利用url中的hash特性實(shí)現(xiàn)
        break
      case "abstract":
        this.history = new AbstractHistory(this, options.base) // 這種模式原理暫不清楚
        break
      default:
        if (process.env.NODE_ENV !== "production") {
          assert(false, `invalid mode: ${mode}`)
        }
    }
  }
  ...
  // 一些api方法,你應(yīng)該很熟悉,$router.push(...)
  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)
  }
  ...
}

我們創(chuàng)建的路由都是VueRouter類的實(shí)例化,用來(lái)管理我們的【key-components-view】,一個(gè)key(代碼中的path)對(duì)應(yīng)一個(gè)組件,view也就是在template里面占個(gè)坑,用來(lái)根據(jù)key展示對(duì)應(yīng)的組件,實(shí)例上的func讓我們可以控制路由,也就是官網(wǎng)的api
說(shuō)簡(jiǎn)單點(diǎn),路由就是一個(gè)‘輪播圖’,emmmmmm,說(shuō)輪播好像也不過(guò)分哈,寫個(gè)循環(huán)切換key的func就是‘輪播了’,而key就是輪播的index,手動(dòng)滑稽。那么,vue-router是如何實(shí)現(xiàn)不發(fā)送請(qǐng)求就更新視圖的呢,讓我們來(lái)看看vue如何使用路由的
實(shí)例化后的路由輸出:區(qū)分下route和router

2. router工作原理
如果你要使用到router,你會(huì)在實(shí)例化Vue的參數(shù)options中加入router

// main.js
improt xxx from xxx
import router from xxx
new Vue({
  el: "#app",
  router: router,
  components: { App },
  template: ""
})

那,Vue是如何使用這個(gè)參數(shù)呢,vue-router是作為插件加入使用的,通過(guò)mixin(混合)來(lái)影響每一個(gè)Vue實(shí)例化,在beforeCreate 鉤子的時(shí)候就會(huì)完成router的初始化,從參數(shù)獲取router -> 調(diào)用init初始化 -> 加入響應(yīng)式(defineReactive方法在vue源碼用的很多,也是底層實(shí)現(xiàn)響應(yīng)式的核心方法)

// 源碼install.js
Vue.mixin({
    beforeCreate () {
      if (isDef(this.$options.router)) {
        this._routerRoot = this
        this._router = this.$options.router   // 獲取options里面的router配置
        this._router.init(this)               // 初始化,這個(gè)init是VueRouter類里面的方法,實(shí)例化后會(huì)繼承這個(gè)方法,方法代碼見下方 
        Vue.util.defineReactive(this, "_route", this._router.history.current) // 這個(gè)是把_route加入數(shù)據(jù)監(jiān)控,所以你可以watch到_route
      } else {
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
      }
      registerInstance(this, this)
    },
    destroyed () {
      registerInstance(this)
    }
  })

初始化會(huì)做些什么:
-判斷主程序狀態(tài)(已經(jīng)初始化了的vue實(shí)例不會(huì)再重新初始化路由,也就是你不能手動(dòng)去再init一次)
-把實(shí)例加入內(nèi)置數(shù)組
-判斷history的類型,做一些類似優(yōu)化的事,比如hash模式的setupListeners方法,就是延遲監(jiān)聽hashChange事件,等到vue完成掛載再監(jiān)聽,太細(xì)節(jié)不用深入
-listen定義一個(gè)callback,listen是定義在最底層History類上的,作用就是定義一個(gè)callback,listen會(huì)在需要的時(shí)候被調(diào)用,在路由發(fā)生變化的時(shí)候會(huì)執(zhí)行這個(gè)callback

// 源碼index.js
export default class VueRouter {
...
init (app: any /* Vue component instance */) {
    process.env.NODE_ENV !== "production" && assert(
      install.installed,
      `not installed. Make sure to call `Vue.use(VueRouter)` ` +
      `before creating root instance.`
    )

    this.apps.push(app)   // 這個(gè)apps存儲(chǔ)了讓所有的Vue實(shí)例化(根組件),后面遍歷的時(shí)候,會(huì)把當(dāng)前標(biāo)記route掛到所有根組件的,也就是 vm._route 也是 vm._router.history.current

    // main app already initialized.
    if (this.app) {
      return
    }

    this.app = app

    const history = this.history

    if (history instanceof HTML5History) {
      history.transitionTo(history.getCurrentLocation())
    } else if (history instanceof HashHistory) {
      const setupHashListener = () => {
        history.setupListeners()
      }
      history.transitionTo(
        history.getCurrentLocation(),
        setupHashListener,
        setupHashListener
      )
    }

    history.listen(route => {               // 注意這個(gè)listen會(huì)在后面用到
      this.apps.forEach((app) => {
        app._route = route                  // 根組件全部獲取當(dāng)前route
      })
    })
  }
...
}

關(guān)于route的變化過(guò)程會(huì)在下面具體模式中說(shuō)明,這里先跳過(guò),接下來(lái)先說(shuō)vue拿到router后,怎么使用router來(lái)渲染組件的

3. vue如何使用router的

在安裝vue-router插件的時(shí)候

export function install (Vue) {
  ...
  Vue.component("RouterView", View)  //  &  你應(yīng)該很熟悉,本質(zhì)就是vue組件,看源碼之前我的猜測(cè)也是組件
  Vue.component("RouterLink", Link)
  ...
}

router-link你不一定會(huì)使用,但是router-view你肯定會(huì)使用,它就是作為"窗口"的存在,來(lái)渲染你需要展示的組件。
那,從這個(gè)組件開始說(shuō),一個(gè)前提條件是:vnode是通過(guò)render來(lái)創(chuàng)建的,也就是說(shuō)改變_route的值會(huì)執(zhí)行render函數(shù),Router-View這個(gè)組件定義了自己的render,省略了大部分代碼,這兩行夠了,你最終通過(guò)看到的視圖就是這么來(lái)的

// vue源碼render.js
export function renderMixin (Vue: Class) {
...
vnode = render.call(vm._renderProxy, vm.$createElement)
...
}
// router源碼 view.js
render (_, { props, children, parent, data }) {
...
const h = parent.$createElement
...
return h(component, data, children)
}
第一種:hashHistory模式

流程

$router.push() --> HashHistory.push() --> History.transitionTo() --> History.updateRoute() --> {app._route = route} --> vm.render()

1. 關(guān)于hash
url中#號(hào)后面的參數(shù),別名哈希值,關(guān)于hash的一些特性

1.改變hash并不會(huì)引起頁(yè)面重載
2.HTTP請(qǐng)求不包括#,所以使用hash不會(huì)影響到其他功能
3.改變#會(huì)改變?yōu)g覽器的訪問歷史
4.window.location.hash可以讀取哈希值
5.JavaScript可以通過(guò)onhashchange監(jiān)聽到hash值的變化,這就意味著可以知道用戶在瀏覽器手動(dòng)改變了hash值

因?yàn)檫@些特性才有的hashHistory
更多關(guān)于hash知識(shí)見 URL的井號(hào) - 阮一峰的網(wǎng)絡(luò)日志

2. hashHistory源碼
首先,這三種模式都是通過(guò)繼承一個(gè)基礎(chǔ)類History來(lái)的

export class HashHistory extends History {
...
}

那,三種模式肯定有相同的屬性,相同的方法,肯定不會(huì)去創(chuàng)建三次所以從一個(gè)基類繼承,然后各自的部分屬性or方法會(huì)有差異,至于History這個(gè)類,我是不會(huì)去細(xì)看的,反正我也看不懂,哈哈哈哈

router上的實(shí)例屬性、方法可以在VueRouter、HashHistory/HTML5History/AbstractHistory、History上找到,這里說(shuō)下HashHistory的幾個(gè)func的實(shí)現(xiàn)、

// router源碼hash.js
export class HTML5History extends History {
...
go (n: number) {
    window.history.go(n)
  }
push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    const { current: fromRoute } = this
    this.transitionTo(location, route => {  // History類上的func
      pushHash(route.fullPath)
      handleScroll(this.router, route, fromRoute, false)
      onComplete && onComplete(route)
    }, onAbort)
  }

function pushHash (path) {
  if (supportsPushState) { // 是否瀏覽器環(huán)境且環(huán)境支持pushstat方法,這個(gè)func下面會(huì)說(shuō)
    pushState(getUrl(path)) // 支持的話往window.history添加一條數(shù)據(jù)
  } else {
    window.location.hash = path // 不支持的話直接修改location的hash
  }
}

  replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    const { current: fromRoute } = this
    this.transitionTo(location, route => {
      replaceHash(route.fullPath)
      handleScroll(this.router, route, fromRoute, false)
      onComplete && onComplete(route)
    }, onAbort)
  }
// 其實(shí)replace和push只有兩個(gè)區(qū)別
1.
window.location.hash = path
window.location.replace(getUrl(path))
2.
if (replace) { // replace調(diào)這個(gè)func會(huì)傳一個(gè)true
  history.replaceState({ key: _key }, "", url)
} else {
  _key = genKey()
  history.pushState({ key: _key }, "", url)
}
...
}

還有一點(diǎn)就是,在初始化hash模式路由的時(shí)候,會(huì)執(zhí)行一個(gè)func,監(jiān)聽hashchange事件

setupListeners () {
    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)
        }
      })
    })
}
第二種:HTML5History模式

HTML5--History 科普

主要是新增的兩個(gè)api

1.History.pushState()


[優(yōu)點(diǎn)寫的清清楚楚]

HTML5History的push、replace跟hash模式的差不多,就不上代碼了
一個(gè)標(biāo)記是否支持HTML5的flag,這么寫的,有需要的可以刨回去用

export const supportsPushState = inBrowser && (function () {
  const ua = window.navigator.userAgent

  if (
    (ua.indexOf("Android 2.") !== -1 || ua.indexOf("Android 4.0") !== -1) &&
    ua.indexOf("Mobile Safari") !== -1 &&
    ua.indexOf("Chrome") === -1 &&
    ua.indexOf("Windows Phone") === -1
  ) {
    return false
  }

  return window.history && "pushState" in window.history
})()

還有一個(gè)就是scrollBehavior,用來(lái)記錄路由跳轉(zhuǎn)的時(shí)候滾動(dòng)條的位置,這個(gè)只能在HTML5模式下使用,即支持pushState方法的時(shí)候,部分博客說(shuō)只有在HTML5History下才能使用,這個(gè)等我明天驗(yàn)證一下,我個(gè)人覺得支持HTML5就可以了

2.History.replaceState()


說(shuō)的也很直觀,就是不創(chuàng)新新紀(jì)錄而覆蓋一條記錄,just do it

結(jié)束語(yǔ)

別問第三種情況(我是誰(shuí)、我在哪、誰(shuí)打我)

我兜子好沃,早知道不做前端了~

在學(xué)習(xí)router源碼的時(shí)候閱讀了熵與單子的代碼本的文章,看完這篇文章配合源碼基本都可以很好掌握vue-router的大概,感謝作者,另外說(shuō)明下本文由本人學(xué)習(xí)結(jié)束后加上自己的理解一字一字敲出來(lái)的,可能有些相似之處,侵刪請(qǐng)聯(lián)系我,寫文章的目的是看看自己能否表述清楚,對(duì)知識(shí)點(diǎn)的掌握情況,講的不對(duì)的地方,請(qǐng)各位大佬指正~

~感謝潘童鞋的指導(dǎo)(^▽^)

當(dāng)然,我也稀罕你的小??,點(diǎn)個(gè)贊再走咯~

以上圖片均來(lái)自MDN網(wǎng)頁(yè)截圖、vue官網(wǎng)截圖、百度首頁(yè)截圖,不存在版權(quán)問題 /滑稽

【注】:內(nèi)容有不當(dāng)或者錯(cuò)誤處請(qǐng)指正~轉(zhuǎn)載請(qǐng)注明出處~謝謝合作!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/101915.html

相關(guān)文章

  • 簡(jiǎn)述vue-router實(shí)現(xiàn)原理

    摘要:源碼解讀閱讀請(qǐng)關(guān)注下代碼注釋打個(gè)廣告哪位大佬教我下怎么排版啊,不會(huì)弄菜單二級(jí)導(dǎo)航撲通是什么首先,你會(huì)從源碼里面引入,然后再傳入?yún)?shù)實(shí)例化一個(gè)路由對(duì)象源碼基礎(chǔ)類源碼不選擇模式會(huì)默認(rèn)使用模式非瀏覽器環(huán)境默認(rèn)環(huán)境根據(jù)參數(shù)選擇三種模式的一種根據(jù)版 router源碼解讀 閱讀請(qǐng)關(guān)注下代碼注釋 打個(gè)廣告:哪位大佬教我下sf怎么排版啊,不會(huì)弄菜單二級(jí)導(dǎo)航(撲通.gif) showImg(https:...

    Ajian 評(píng)論0 收藏0
  • 前端面試題總結(jié)(js、html、小程序、React、ES6、Vue、算法、全棧熱門視頻資源)

    摘要:并總結(jié)經(jīng)典面試題集各種算法和插件前端視頻源碼資源于一身的文檔,優(yōu)化項(xiàng)目,在瀏覽器端的層面上提升速度,幫助初中級(jí)前端工程師快速搭建項(xiàng)目。 本文是關(guān)注微信小程序的開發(fā)和面試問題,由基礎(chǔ)到困難循序漸進(jìn),適合面試和開發(fā)小程序。并總結(jié)vue React html css js 經(jīng)典面試題 集各種算法和插件、前端視頻源碼資源于一身的文檔,優(yōu)化項(xiàng)目,在瀏覽器端的層面上提升速度,幫助初中級(jí)前端工程師快...

    pumpkin9 評(píng)論0 收藏0
  • 前端面試題總結(jié)(js、html、小程序、React、ES6、Vue、算法、全棧熱門視頻資源)

    摘要:并總結(jié)經(jīng)典面試題集各種算法和插件前端視頻源碼資源于一身的文檔,優(yōu)化項(xiàng)目,在瀏覽器端的層面上提升速度,幫助初中級(jí)前端工程師快速搭建項(xiàng)目。 本文是關(guān)注微信小程序的開發(fā)和面試問題,由基礎(chǔ)到困難循序漸進(jìn),適合面試和開發(fā)小程序。并總結(jié)vue React html css js 經(jīng)典面試題 集各種算法和插件、前端視頻源碼資源于一身的文檔,優(yōu)化項(xiàng)目,在瀏覽器端的層面上提升速度,幫助初中級(jí)前端工程師快...

    Carson 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<