前端路由 & 實(shí)現(xiàn)原理

前端路由

  • 什么是路由:地址欄的變化、URL到函數(shù)的映射

后端路由:又稱服務(wù)器路由,服務(wù)器接收前端發(fā)來(lái)的http請(qǐng)求后,會(huì)根據(jù)url找到相應(yīng)的映射函數(shù),然后執(zhí)行函數(shù),并將返回值發(fā)回給客戶端。對(duì)于靜態(tài)資源來(lái)說(shuō),映射函數(shù)就是一個(gè)文件讀取操作;對(duì)于動(dòng)態(tài)資源,映射函數(shù)可能是一個(gè)數(shù)據(jù)庫(kù)讀取操作,或者是數(shù)據(jù)處理操作;根據(jù)讀取的數(shù)據(jù),在服務(wù)端使用相應(yīng)的模板來(lái)渲染頁(yè)面,再返回渲染完畢的頁(yè)面【??這是早期的做法】

如訪問(wèn)http://xxx/about,服務(wù)器收到后,解析出/about,尋找到對(duì)應(yīng)的邏輯,程序吧請(qǐng)求交給對(duì)應(yīng)處理邏輯,完成一次路由分發(fā)。

  • 好處:安全性好、SEO好;

  • 缺點(diǎn):加大服務(wù)器壓力,不利于用戶體驗(yàn)

前端路由:路由的映射函數(shù);當(dāng)訪問(wèn)不同的路徑,會(huì)顯示不同的頁(yè)面組件。控制頁(yè)面跳轉(zhuǎn)

單頁(yè)應(yīng)用路由的特點(diǎn):通過(guò)改變url,不會(huì)向后端發(fā)送新的請(qǐng)求,更新頁(yè)面視圖,所有頁(yè)面均在客戶端渲染

  • 更新地址欄,但是不重新請(qǐng)求頁(yè)面

  • 兩種模式都是使用瀏覽器接口實(shí)現(xiàn)的,除此之外,vue-router還為非瀏覽器提供了abstract模式,原理為用一個(gè)數(shù)組stack模擬出瀏覽器歷史記錄棧的功能。

缺點(diǎn):使用前進(jìn)后退時(shí)會(huì)重新發(fā)請(qǐng)求,沒(méi)有合理利用緩存。

  • hash:#本身以及后面的字符稱為hash,可以通過(guò)window.location來(lái)獲取

    ??a標(biāo)簽和router-link區(qū)別,hash模式只關(guān)心#后面的路徑-

    早期路由實(shí)現(xiàn)基于location.hash實(shí)現(xiàn),即#及后面的部分

    • url中的hash只是客戶端的一種狀態(tài),當(dāng)向服務(wù)器發(fā)出請(qǐng)求,hash部分不會(huì)被發(fā)送;

    • hash值改變,會(huì)在瀏覽器訪問(wèn)歷史中增加記錄,可以通過(guò)瀏覽器的前進(jìn)后退控制hash切換

    • 可以通過(guò)hashchange事件來(lái)監(jiān)聽(tīng)hash的變化

    • 觸發(fā)的方式:<a href="#xxx">、location.hash="#xxx",

      http://xxx/about,#后面會(huì)被服務(wù)器忽略,但是JavaScript通過(guò)window.location.hash讀取到,得到響應(yīng)邏輯

  • history API

    • history.back()、history.forward()、history.go(xx)

    • html5提供了history API,主要有history.pushState(狀態(tài)對(duì)象,標(biāo)題,URL)、history.replaceState(),可以在不進(jìn)行刷新的情況下操作歷史記錄。不同的是,前者是增加歷史記錄,后者是替換當(dāng)前歷史記錄

  • 對(duì)比:hash兼容性更好;history更正式,可以設(shè)置與當(dāng)前url同源的任意url,路徑更美觀;基于hash的路由不用對(duì)服務(wù)器做改動(dòng),history的路由要對(duì)服務(wù)器做相應(yīng)配置。

    • ????hash會(huì)檢測(cè)#之后的路徑,如果發(fā)現(xiàn)一致,不會(huì)重新加載,只有但與之前是不一樣才會(huì)觸發(fā)記錄添加到棧中;history可以反復(fù)重新加載同一個(gè)頁(yè)面,pushState也會(huì)添加到棧中

    • pushState設(shè)置的新url可以與當(dāng)前url同源的任意url;hash只可修改#后面的部分,所以只可設(shè)置與當(dāng)前同文檔的url

    • pushState通過(guò)stateObject可以添加任意類型的數(shù)據(jù)到記錄中;hash只可以添加短字符

    • pushState可以額外設(shè)置title屬性供后續(xù)使用

  • abstract:非瀏覽器

  • 關(guān)于history的一個(gè)問(wèn)題:?jiǎn)雾?yè)應(yīng)用的理想場(chǎng)景是,僅在進(jìn)入應(yīng)用時(shí)加載index.html,后續(xù)的網(wǎng)絡(luò)操作通過(guò)ajax完成,不會(huì)根據(jù)url重新請(qǐng)求頁(yè)面,但是難免遇到特殊情況:用戶在地址欄輸入地址并回車,瀏覽器重新加載應(yīng)用等;

    1. hash模式僅改變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)求頁(yè)面的情況不會(huì)有問(wèn)題

    2. history模式會(huì)將url修改得和正常請(qǐng)求后端的url一樣http://oursite.com/user/id,在此情況下向后端發(fā)送請(qǐng)求,如果后端沒(méi)有配置對(duì)應(yīng)的user/id路由處理,就會(huì)返回404錯(cuò)誤

    3. ??官方推薦的解決方法:在服務(wù)端增加一個(gè)覆蓋所有情況的候選資源:如果url匹配不到任何靜態(tài)資源,則應(yīng)該返回同一個(gè)index.html頁(yè)面,這個(gè)頁(yè)面就是App依賴的頁(yè)面。這樣服務(wù)器就不會(huì)返回404錯(cuò)誤頁(yè)面,因?yàn)閷?duì)所有頁(yè)面都返回index.html文件。為了應(yīng)對(duì)這種情況,在vue應(yīng)用里面覆蓋所有的路由情況,然后給出一個(gè)404頁(yè)面,或者,如果使用nodejs做后臺(tái),可以使用服務(wù)端的路由來(lái)匹配url,當(dāng)沒(méi)有匹配到路由時(shí)返回404,從而實(shí)現(xiàn)fallback。

- hash & history 前端路由實(shí)現(xiàn)方式

更新視圖但不重新請(qǐng)求頁(yè)面”是前端路由原理的核心

模式參數(shù):

vue-router中通過(guò)mode來(lái)控制路由的實(shí)現(xiàn)模式

  • mode只是一個(gè)標(biāo)記,mode:history: HashHistory | HTML5History | AbstractHistory;根據(jù)history的類別執(zhí)行相應(yīng)的初始化操作和監(jiān)聽(tīng);VueRouter類暴露的以下方法實(shí)際是調(diào)用具體history對(duì)象的方法(push & replace

  • 初始化history前,會(huì)進(jìn)行校驗(yàn),瀏覽器不支持HTML5History(通過(guò)suportsPushState變量判斷)則強(qiáng)制為hash模式,非瀏覽器環(huán)境為abstract模式

hash模式:

  • 改變hash不會(huì)重新加載頁(yè)面,不會(huì)被包含在http請(qǐng)求中,指導(dǎo)瀏覽器動(dòng)作的

  • 為hash的改變添加監(jiān)聽(tīng)事件:window.addEventListener("hashchange", funcRef, false)

  • 每一次改變hash(window.location.hash),都會(huì)在瀏覽器的訪問(wèn)歷史中增加一個(gè)記錄

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

  • replace()方法與push()方法不同之處:不是直接對(duì)window.location.hash進(jìn)行賦值,而是調(diào)用window.location.replace方法將路由進(jìn)行替換

  • 監(jiān)聽(tīng)地址欄:我們可以通過(guò)輸入U(xiǎn)RL訪問(wèn),因此要添加路由監(jiān)聽(tīng)并觸發(fā)響應(yīng)行為 通過(guò)setupListeners實(shí)現(xiàn)

setupListeners () {
     window.addEventListener('hashchange', () => {
     if (!ensureSlash()) {
     return
     }
     this.transitionTo(getHash(), route => {
     replaceHash(route.fullPath) // 相當(dāng)于調(diào)用了replaceHash
     })
     })
    }

history模式:

History interface是瀏覽器歷史記錄棧提供的接口,通過(guò)back(), forward(), go()等方法,我們可以讀取瀏覽器歷史記錄棧的信息,進(jìn)行各種跳轉(zhuǎn)操作。

新的方法:pushState(), replaceState()對(duì)瀏覽器歷史記錄棧進(jìn)行修改:

window.history.pushState(stateObject, title, URL)
window.history.replaceState(stateObject, title, URL)

代碼結(jié)構(gòu)以及更新視圖的邏輯與hash模式基本類似,只不過(guò)將對(duì)window.location.hash直接進(jìn)行賦值window.location.replace()改為了調(diào)用history.pushState()history.replaceState()方法。

abstract模式:

原理為用一個(gè)數(shù)組stack模擬出瀏覽器歷史記錄棧的功能。

history VS hash

  • ·pushState設(shè)置的新URL可以是與當(dāng)前URL同源的任意URL;而hash只可修改#后面的部分,故只可設(shè)置與當(dāng)前同文檔的URL

  • pushState設(shè)置的新URL可以與當(dāng)前URL一模一樣,這樣也會(huì)把記錄添加到棧中;而hash設(shè)置的新值必須與原來(lái)不一樣才會(huì)觸發(fā)記錄添加到棧中

  • pushState通過(guò)stateObject可以添加任意類型的數(shù)據(jù)到記錄中;而hash只可添加短字符串

  • pushState可額外設(shè)置title屬性供后續(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),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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