摘要:源碼解讀閱讀請關注下代碼注釋打個廣告哪位大佬教我下怎么排版啊,不會弄菜單二級導航撲通是什么首先,你會從源碼里面引入,然后再傳入參數實例化一個路由對象源碼基礎類源碼不選擇模式會默認使用模式非瀏覽器環境默認環境根據參數選擇三種模式的一種根據版
router源碼解讀
閱讀請關注下代碼注釋
打個廣告:哪位大佬教我下sf怎么排版啊,不會弄菜單二級導航(撲通.gif)
1. router是什么
首先,你會從源碼里面引入Router,然后再傳入參數實例化一個路由對象
// router/index.js import Router from "vue-router" new Router({...}) ...
源碼基礎類:
// 源碼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" // 不選擇模式會默認使用hash模式 this.fallback = mode === "history" && !supportsPushState && options.fallback !== false if (this.fallback) { mode = "hash" } if (!inBrowser) { // 非瀏覽器環境默認nodejs環境 mode = "abstract" } this.mode = mode switch (mode) { // 根據參數選擇三種模式的一種 case "history": this.history = new HTML5History(this, options.base) // 根據HTML5版History的方法和屬性實現的模式 break case "hash": this.history = new HashHistory(this, options.base, this.fallback) // 利用url中的hash特性實現 break case "abstract": this.history = new AbstractHistory(this, options.base) // 這種模式原理暫不清楚 break default: if (process.env.NODE_ENV !== "production") { assert(false, `invalid mode: ${mode}`) } } } ... // 一些api方法,你應該很熟悉,$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) } ... }
我們創建的路由都是VueRouter類的實例化,用來管理我們的【key-components-view】,一個key(代碼中的path)對應一個組件,view也就是
說簡單點,路由就是一個‘輪播圖’,emmmmmm,說輪播好像也不過分哈,寫個循環切換key的func就是‘輪播了’,而key就是輪播的index,手動滑稽。那么,vue-router是如何實現不發送請求就更新視圖的呢,讓我們來看看vue如何使用路由的
實例化后的路由輸出:區分下route和router
2. router工作原理
如果你要使用到router,你會在實例化Vue的參數options中加入router
// main.js improt xxx from xxx import router from xxx new Vue({ el: "#app", router: router, components: { App }, template: "" })
那,Vue是如何使用這個參數呢,vue-router是作為插件加入使用的,通過mixin(混合)來影響每一個Vue實例化,在beforeCreate 鉤子的時候就會完成router的初始化,從參數獲取router -> 調用init初始化 -> 加入響應式(defineReactive方法在vue源碼用的很多,也是底層實現響應式的核心方法)
// 源碼install.js Vue.mixin({ beforeCreate () { if (isDef(this.$options.router)) { this._routerRoot = this this._router = this.$options.router // 獲取options里面的router配置 this._router.init(this) // 初始化,這個init是VueRouter類里面的方法,實例化后會繼承這個方法,方法代碼見下方 Vue.util.defineReactive(this, "_route", this._router.history.current) // 這個是把_route加入數據監控,所以你可以watch到_route } else { this._routerRoot = (this.$parent && this.$parent._routerRoot) || this } registerInstance(this, this) }, destroyed () { registerInstance(this) } })
初始化會做些什么:
-判斷主程序狀態(已經初始化了的vue實例不會再重新初始化路由,也就是你不能手動去再init一次)
-把實例加入內置數組
-判斷history的類型,做一些類似優化的事,比如hash模式的setupListeners方法,就是延遲監聽hashChange事件,等到vue完成掛載再監聽,太細節不用深入
-listen定義一個callback,listen是定義在最底層History類上的,作用就是定義一個callback,listen會在需要的時候被調用,在路由發生變化的時候會執行這個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) // 這個apps存儲了讓所有的Vue實例化(根組件),后面遍歷的時候,會把當前標記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 => { // 注意這個listen會在后面用到 this.apps.forEach((app) => { app._route = route // 根組件全部獲取當前route }) }) } ... }
關于route的變化過程會在下面具體模式中說明,這里先跳過,接下來先說vue拿到router后,怎么使用router來渲染組件的
3. vue如何使用router的
在安裝vue-router插件的時候
export function install (Vue) { ... Vue.component("RouterView", View) //& 你應該很熟悉,本質就是vue組件,看源碼之前我的猜測也是組件 Vue.component("RouterLink", Link) ... }
router-link你不一定會使用,但是router-view你肯定會使用,它就是作為"窗口"的存在,來渲染你需要展示的組件。
那,從這個組件開始說,一個前提條件是:vnode是通過render來創建的,也就是說改變_route的值會執行render函數,Router-View這個組件定義了自己的render,省略了大部分代碼,這兩行夠了,你最終通過
// vue源碼render.js export function renderMixin (Vue: Class第一種:hashHistory模式) { ... vnode = render.call(vm._renderProxy, vm.$createElement) ... } // router源碼 view.js render (_, { props, children, parent, data }) { ... const h = parent.$createElement ... return h(component, data, children) }
流程
$router.push() --> HashHistory.push() --> History.transitionTo() --> History.updateRoute() --> {app._route = route} --> vm.render()
1. 關于hash
url中#號后面的參數,別名哈希值,關于hash的一些特性
1.改變hash并不會引起頁面重載 2.HTTP請求不包括#,所以使用hash不會影響到其他功能 3.改變#會改變瀏覽器的訪問歷史 4.window.location.hash可以讀取哈希值 5.JavaScript可以通過onhashchange監聽到hash值的變化,這就意味著可以知道用戶在瀏覽器手動改變了hash值
因為這些特性才有的hashHistory
更多關于hash知識見 URL的井號 - 阮一峰的網絡日志
2. hashHistory源碼
首先,這三種模式都是通過繼承一個基礎類History來的
export class HashHistory extends History { ... }
那,三種模式肯定有相同的屬性,相同的方法,肯定不會去創建三次所以從一個基類繼承,然后各自的部分屬性or方法會有差異,至于History這個類,我是不會去細看的,反正我也看不懂,哈哈哈哈
router上的實例屬性、方法可以在VueRouter、HashHistory/HTML5History/AbstractHistory、History上找到,這里說下HashHistory的幾個func的實現、
// 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) { // 是否瀏覽器環境且環境支持pushstat方法,這個func下面會說 pushState(getUrl(path)) // 支持的話往window.history添加一條數據 } 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) } // 其實replace和push只有兩個區別 1. window.location.hash = path window.location.replace(getUrl(path)) 2. if (replace) { // replace調這個func會傳一個true history.replaceState({ key: _key }, "", url) } else { _key = genKey() history.pushState({ key: _key }, "", url) } ... }
還有一點就是,在初始化hash模式路由的時候,會執行一個func,監聽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 科普
主要是新增的兩個api
1.History.pushState()
[優點寫的清清楚楚]
HTML5History的push、replace跟hash模式的差不多,就不上代碼了
一個標記是否支持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 })()
還有一個就是scrollBehavior,用來記錄路由跳轉的時候滾動條的位置,這個只能在HTML5模式下使用,即支持pushState方法的時候,部分博客說只有在HTML5History下才能使用,這個等我明天驗證一下,我個人覺得支持HTML5就可以了
2.History.replaceState()
說的也很直觀,就是不創新新紀錄而覆蓋一條記錄,just do it
別問第三種情況(我是誰、我在哪、誰打我)
我兜子好沃,早知道不做前端了~
在學習router源碼的時候閱讀了熵與單子的代碼本的文章,看完這篇文章配合源碼基本都可以很好掌握vue-router的大概,感謝作者,另外說明下本文由本人學習結束后加上自己的理解一字一字敲出來的,可能有些相似之處,侵刪請聯系我,寫文章的目的是看看自己能否表述清楚,對知識點的掌握情況,講的不對的地方,請各位大佬指正~
~感謝潘童鞋的指導(^▽^)
當然,我也稀罕你的小??,點個贊再走咯~
以上圖片均來自MDN網頁截圖、vue官網截圖、百度首頁截圖,不存在版權問題 /滑稽
【注】:內容有不當或者錯誤處請指正~轉載請注明出處~謝謝合作!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/101915.html
摘要:源碼解讀閱讀請關注下代碼注釋打個廣告哪位大佬教我下怎么排版啊,不會弄菜單二級導航撲通是什么首先,你會從源碼里面引入,然后再傳入參數實例化一個路由對象源碼基礎類源碼不選擇模式會默認使用模式非瀏覽器環境默認環境根據參數選擇三種模式的一種根據版 router源碼解讀 閱讀請關注下代碼注釋 打個廣告:哪位大佬教我下sf怎么排版啊,不會弄菜單二級導航(撲通.gif) showImg(https:...
摘要:并總結經典面試題集各種算法和插件前端視頻源碼資源于一身的文檔,優化項目,在瀏覽器端的層面上提升速度,幫助初中級前端工程師快速搭建項目。 本文是關注微信小程序的開發和面試問題,由基礎到困難循序漸進,適合面試和開發小程序。并總結vue React html css js 經典面試題 集各種算法和插件、前端視頻源碼資源于一身的文檔,優化項目,在瀏覽器端的層面上提升速度,幫助初中級前端工程師快...
摘要:并總結經典面試題集各種算法和插件前端視頻源碼資源于一身的文檔,優化項目,在瀏覽器端的層面上提升速度,幫助初中級前端工程師快速搭建項目。 本文是關注微信小程序的開發和面試問題,由基礎到困難循序漸進,適合面試和開發小程序。并總結vue React html css js 經典面試題 集各種算法和插件、前端視頻源碼資源于一身的文檔,優化項目,在瀏覽器端的層面上提升速度,幫助初中級前端工程師快...
閱讀 919·2021-11-25 09:43
閱讀 1290·2021-11-17 09:33
閱讀 3008·2019-08-30 15:44
閱讀 3309·2019-08-29 17:16
閱讀 476·2019-08-28 18:20
閱讀 1632·2019-08-26 13:54
閱讀 552·2019-08-26 12:14
閱讀 2172·2019-08-26 12:14