Vue-Router探索

介紹

vue-router是一個(gè)vue插件。其實(shí)質(zhì)是在location.hash、location.replace、HTML5 historyAPI、window.onpopstate、window.onhashchange上做的封裝,通過一定的規(guī)則映射到對(duì)應(yīng)的方法上,同時(shí)通過監(jiān)聽變化,從而實(shí)現(xiàn)單頁路由。


裝載

使用use(VueRouter)注入Vue中,use方法會(huì)檢測(cè)注入插件VueRouter內(nèi)的install方法(瀏覽器環(huán)境會(huì)自動(dòng)調(diào)用,node環(huán)境需要手動(dòng)調(diào)用)

第一步:Install解析(對(duì)應(yīng)目錄結(jié)構(gòu)的install.js)
該方法內(nèi)主要做了以下三件事:

  1. 對(duì)Vue實(shí)例混入beforeCreate鉤子操作(在Vue的生命周期階段會(huì)被調(diào)用)
  2. 通過Vue.prototype定義router、route 屬性(方便所有組件可以獲取這兩個(gè)屬性)
    注:$route為當(dāng)前router跳轉(zhuǎn)對(duì)象里面可以獲取name、path、query、params等(只讀)
    $router為VueRouter實(shí)例,想要導(dǎo)航到不同URL,則使用$router.push方法
  3. Vue上注冊(cè)router-link和router-view兩個(gè)組件

第二步:生成router實(shí)例
生成實(shí)例過程中,主要做了以下兩件事

  1. 根據(jù)配置數(shù)組(傳入的routes)生成路由配置記錄表。(根據(jù)傳入的 routes 配置生成對(duì)應(yīng)的路由 map,然后直接返回了 match 匹配函數(shù))
  2. 根據(jù)不同模式生成監(jiān)控路由變化的History對(duì)象
    注:History類由HTML5History、HashHistory、AbstractHistory三類繼承,默認(rèn)為hash模式

history/base.js實(shí)現(xiàn)了基本history的操作
history/hash.js,history/html5.js和history/abstract.js繼承了base,只是根據(jù)不同的模式封裝了一些基本操作

第三步:生成vue實(shí)例
進(jìn)入Vue的生命周期,第一步Vue-Router對(duì)Vue混入的beforeCreate鉤子會(huì)執(zhí)行。
一開始驗(yàn)證vue是否有router對(duì)象了,如果有,就不再初始化了。否則,先將routerRoot指向根組件,將router對(duì)象掛載到根組件元素_router上(this._routerRoot = this; this._router = this.$options.router)然后初始化,建立路由監(jiān)控,劫持?jǐn)?shù)據(jù)_route,一旦_route數(shù)據(jù)發(fā)生變化后,通知router-view執(zhí)行render方法。
三步走完初始化結(jié)束,界面將顯示默認(rèn)首頁。

關(guān)系圖

文件結(jié)構(gòu)

vue-router src文件結(jié)構(gòu)

components下是兩個(gè)組件router-view和router-link;history是路由方式的封裝,提供三種方式;util下主要是各種功能類和功能函數(shù);create-matcher和create-router-map是生成匹配表;index是VueRouter類,也整個(gè)插件的入口;Install 提供安裝的方法


路由更新觸發(fā)方式

一、主動(dòng)觸發(fā)

router-link綁定了click方法,觸發(fā)history.push或者h(yuǎn)istory.replace,從而觸發(fā)history.transitionTo。(這里的history并不是H5的History對(duì)象,而是vue-router里定義的history對(duì)象)

transitionTo用于處理路由轉(zhuǎn)換,其中包含了updateRoute用于更新_route。

在beforeCreate中有劫持_route的方法,當(dāng)_route變化后,觸發(fā)router-view的變化。

二、地址變化(如:在瀏覽器地址欄直接輸入地址)

HashHistory和HTML5History會(huì)分別監(jiān)控hashchange和popstate來對(duì)路由變化作對(duì)用的處理 。

HashHistory和HTML5History捕獲到變化后會(huì)對(duì)應(yīng)執(zhí)行push或replace方法,從而調(diào)用transitionTo,剩下的就和上面主動(dòng)觸發(fā)一樣啦。


路由更新詳解

在使用單頁面前端路由跳轉(zhuǎn)時(shí),提供了兩種方式:Hash模式History模式
那為什么這兩種方式能夠?qū)崿F(xiàn)試圖更新不跳轉(zhuǎn),其原因在于:

1、Hash模式
hash(#)是URL 的錨點(diǎn),代表的是網(wǎng)頁中的一個(gè)位置,單單改變#后的部分,瀏覽器只會(huì)滾動(dòng)到相應(yīng)位置,不會(huì)重新加載網(wǎng)頁,也就是說 #是用來指導(dǎo)瀏覽器動(dòng)作的,對(duì)服務(wù)器端完全無用,HTTP請(qǐng)求中也不會(huì)不包括#;同時(shí)每一次改變#后的部分,都會(huì)在瀏覽器的訪問歷史中增加一個(gè)記錄,使用”后退”按鈕,就可以回到上一個(gè)位置。
(hash雖然出現(xiàn)在URL中,但不會(huì)被包括在HTTP請(qǐng)求中。它是用來指導(dǎo)瀏覽器動(dòng)作的,對(duì)服務(wù)器端完全無用,因此,改變hash不會(huì)重新加載頁面)

HashHistory.push()流程:$router.push() --> HashHistory.push() --> History.transitionTo() --> History.updateRoute() --> vm.render() (調(diào)用window.location.hash修改)

HashHistory.replace() 實(shí)現(xiàn)基本與push類似,不同之處在于,它并不是將新路由添加到瀏覽器訪問歷史的棧頂,而是調(diào)用window.location.replace替換掉當(dāng)前的路由

另外,HashHistory通過setupListeners,設(shè)置監(jiān)聽了瀏覽器事件hashchange來應(yīng)對(duì)用戶直接在地址欄輸入改變路由,調(diào)用的函數(shù)為replaceHash,即在瀏覽器地址欄中直接輸入路由相當(dāng)于代碼調(diào)用了replace()方法

2、History模式

HTML5 History API提供了一種功能,能讓開發(fā)人員在不刷新整個(gè)頁面的情況下修改站點(diǎn)的URL,就是利用 history.pushState API 來完成 URL 跳轉(zhuǎn)而無須重新加載頁面;

3、抽象模式

抽象模式是屬于最簡單的處理了,因?yàn)椴簧婕昂蜑g覽器地址相關(guān)記錄關(guān)聯(lián)在一起;整體流程依舊和 HashHistory 是一樣的,只是這里通過數(shù)組來模擬瀏覽器歷史記錄堆棧信息。


Hash模式和History模式比較

除了#比較丑外,調(diào)用history.pushState()相比于直接修改hash的優(yōu)勢(shì)有:

  1. pushState設(shè)置的新URL可以是與當(dāng)前URL同源的任意URL;而hash只可修改#后面的部分,故只可設(shè)置與當(dāng)前同文檔的URL
  2. pushState設(shè)置的新URL可以與當(dāng)前URL一模一樣,這樣也會(huì)把記錄添加到棧中;而hash設(shè)置的新值必須與原來不一樣才會(huì)觸發(fā)記錄添加到棧中
  3. pushState通過stateObject可以添加任意類型的數(shù)據(jù)到記錄中;而hash只可添加短字符串
  4. pushState可額外設(shè)置title屬性供后續(xù)使用

問題? 因?yàn)閔ash模式僅改變hash部分的內(nèi)容,而hash部分是不會(huì)包含在HTTP請(qǐng)求中的:
http://oursite.com/#/user/id // 如重新請(qǐng)求只會(huì)發(fā)送http://oursite.com/故在hash模式下遇到根據(jù)URL請(qǐng)求頁面的情況不會(huì)有問題。

而history模式則會(huì)將URL修改得就和正常請(qǐng)求后端的URL一樣http://oursite.com/user/id會(huì)報(bào)404error,這個(gè)時(shí)候需要vue應(yīng)用覆蓋所有情況給出一個(gè)404頁面或者是node作后臺(tái)的話中間過濾一層。


匹配優(yōu)先級(jí)

有時(shí)候,同一個(gè)路徑可以匹配多個(gè)路由,此時(shí),匹配的優(yōu)先級(jí)就按照路由的定義順序:誰先定義的,誰的優(yōu)先級(jí)就最高


關(guān)于守衛(wèi)**

參數(shù)或查詢的改變并不會(huì)觸發(fā)進(jìn)入/離開的導(dǎo)航守衛(wèi)。不會(huì)應(yīng)用在跳轉(zhuǎn)路由上,而僅僅應(yīng)用在其目標(biāo)上。為redirect前的/a路由添加一個(gè)beforeEach或beforeLeave守衛(wèi)并不會(huì)有任何效果。

  • 全局守衛(wèi)beforeEach 當(dāng)一個(gè)導(dǎo)航觸發(fā)時(shí),全局前置守衛(wèi)按照創(chuàng)建順序調(diào)用。守衛(wèi)是異步解析執(zhí)行,此時(shí)導(dǎo)航在所有守衛(wèi) resolve 完之前一直處于等待中

  • 全局解析守衛(wèi)beforeResolve 和router.beforeEach類似,區(qū)別是在導(dǎo)航被確認(rèn)之前,同時(shí)在所有組件內(nèi)守衛(wèi)和異步路由組件被解析之后,解析守衛(wèi)就被調(diào)用。

  • 全局后置鉤子afterEach和守衛(wèi)不同的是,這些鉤子不會(huì)接受next函數(shù)也不會(huì)改變導(dǎo)航本身。

  • 路由獨(dú)享的守衛(wèi)beforeEnter 與全局前置守衛(wèi)的方法參數(shù)是一樣的,在路由配置上直接定義,直接寫在路由配置里面。

  • 組件內(nèi)的守衛(wèi) beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

完整流程:
1.導(dǎo)航被觸發(fā)
2.在失活的組件里調(diào)用離開守衛(wèi)
3.調(diào)用全局的beforeEach守衛(wèi)
4.在重用的組件里調(diào)用beforeRouteUpdate守衛(wèi)
5.在路由配置里調(diào)用beforeEnter
6.解析異步路由組件
7.在被激活的組件里調(diào)用beforeRouteEnter
8.調(diào)用全局的beforeResolve守衛(wèi)
9.導(dǎo)航被確認(rèn)
10.調(diào)用全局的afterEach鉤子
11.觸發(fā) DOM 更新
12.用創(chuàng)建好的實(shí)例調(diào)用beforeRouteEnter守衛(wèi)中傳給next的回調(diào)函數(shù)。


比較Vue-router與React-router

他們最基本的初衷都是實(shí)現(xiàn)前端路由。所謂前端路由,簡單來說,就是當(dāng)瀏覽器的url產(chǎn)生變化時(shí),不向服務(wù)器進(jìn)行請(qǐng)求,而是直接控制前端頁面產(chǎn)生變化,以期待前端在比如功能切換時(shí),產(chǎn)生類似頁面跳轉(zhuǎn)等效果。

而這里面最基本的,無論是vue-router還是react-router,都要提供一種配置方式,讓使用者可以配置出url路徑和要展示的組件的對(duì)應(yīng)關(guān)系。讓其在用戶更改url時(shí)通過這個(gè)url找到對(duì)應(yīng)的組件,從而有針對(duì)性的在頁面上渲染。

需要注意的是:VUE的路由配置要提供給new VueRouter()對(duì)象,這個(gè)對(duì)象要在全局VUE對(duì)象初始化時(shí)提供;而React路由則需要配置給全局<Router>組件,雖然react-router也提供類似于vue-router典型代碼中的對(duì)象數(shù)組形式的配置方式,但是最終仍是要將配置傳遞給<Router>。一個(gè)是全局配置(VUE),一個(gè)是全局組件(React),這是兩者使用上的根本區(qū)別之一。(vue-router并不提供像JSX這種類html的配置方式,它只能以對(duì)象方式提供路由配置,這也是框架系統(tǒng)不同所決定的)

總結(jié)不同:

  • vue-router是全局配置方式,react-router是全局組件方式。

  • vue-router僅支持對(duì)象形式的配置,react-router支持對(duì)象形式和JSX語法的組件形式配置。

  • vue-router任何路由組件都會(huì)被渲染到<router-view>位置,react-router子組件作為children被傳入父組件,而根組件被渲染到<Router>位置。


參考

原理分析
源碼詳細(xì)分析

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容