淺談前端路由

關(guān)于秋招的一些感想

最近一段時間忙著秋招,一路經(jīng)歷了阿里終面掛,美團(tuán)終面掛等等,感覺自己有點(diǎn)苦逼(尤其是因為沒HC而掛是最難受的)。雖然到現(xiàn)在還沒得到滿意的結(jié)果,但這個過程還是有所收獲的,這是雖然一個自我懷疑的過程,但也是一個不斷彌補(bǔ)自身不足,學(xué)習(xí)的過程,其中與一些面試我的師兄在交流的過程中也是受益匪淺,并且也對自身以后要走的路有了一個更加明確的認(rèn)識,所以前途漫漫還需努力呀!

一、路由

在現(xiàn)代前端開發(fā)中,路由是非常重要的一環(huán)。但路由到底是什么呢?有些說:路由就是指隨著瀏覽器地址欄的變化,展示給用戶的頁面也不相同。這是從路由的用途上來解釋路由是什么的,還有一種說法是:路由就是URL到函數(shù)的映射。這是從路由的實現(xiàn)原理上來解釋路由是什么的。這兩種說法都很有道理,但我個人認(rèn)為還是第二種比較切合我自己對路由的理解吧。

上面已經(jīng)說了上面是路由,而路由本身也經(jīng)歷了不同的發(fā)展階段:

  1. 后端路由
  2. 前端路由

后端路由又可稱之為服務(wù)器端路由,因為對于服務(wù)器來說,當(dāng)接收到客戶端發(fā)來的HTTP請求,就會根據(jù)所請求的相應(yīng)URL,來找到相應(yīng)的映射函數(shù),然后執(zhí)行該函數(shù),并將函數(shù)的返回值發(fā)送給客戶端。對于最簡單的靜態(tài)資源服務(wù)器,可以認(rèn)為,所有URL的映射函數(shù)就是一個文件讀取操作。對于動態(tài)資源,映射函數(shù)可能是一個數(shù)據(jù)庫讀取操作,也可能是進(jìn)行一些數(shù)據(jù)的處理,等等。然后根據(jù)這些讀取的數(shù)據(jù),在服務(wù)器端就使用相應(yīng)的模板來對頁面進(jìn)行渲染后,再返回渲染完畢的頁面。這種方式在早期的前端開發(fā)中非常普遍,它的好處與壞處都很明顯:

  • 好處:安全性好,SEO好。
  • 缺點(diǎn):加大服務(wù)器的壓力,不利于用戶體驗,代碼冗合。

也正是由于后端路由還存在著自己的不足,前端路由才有了屬于自己的一片天地與發(fā)展的空間。對于前端路由來說,路由的映射函數(shù)通常是進(jìn)行一些DOM的顯示和隱藏操作。這樣,當(dāng)訪問不同的路徑的時候,會顯示不同的頁面組件。前端路由主要有以下兩種實現(xiàn)方案:

  • hash
  • history API

當(dāng)然,前端路由也存在缺陷:使用瀏覽器的前進(jìn),后退鍵時會重新發(fā)送請求,來獲取數(shù)據(jù),沒有合理地利用緩存。但總的來說,現(xiàn)在前端路由已經(jīng)是實現(xiàn)路由的主要方式了,我們常用的諸如react-router等前端框架的路由控制都是基于前端路由進(jìn)行開發(fā)的,因此將前端路由進(jìn)行一個了解
還是很有必要的。

二、前端路由的實現(xiàn)

2.1 基于hash

早期的前端路由的實現(xiàn)就是基于location.hash來實現(xiàn)的。其實現(xiàn)原理也很簡單,location.hash的值就是URL中#后面的內(nèi)容。比如下面這個網(wǎng)站,它的location.hash='#me':

https://www.srtian.com#me

此外,hash也存在下面幾個特性:

  • URL中hash值只是客戶端的一種狀態(tài),也就是說當(dāng)向服務(wù)器端發(fā)出請求時,hash部分不會被發(fā)送。
  • hash值的改變,都會在瀏覽器的訪問歷史中增加一個記錄。因此我們能通過瀏覽器的回退、前進(jìn)按鈕控制hash的切換。
  • 我們可以使用hashchange事件來監(jiān)聽hash的變化。

出發(fā)hsah變化的方式也有兩種,一種是通過a標(biāo)簽,并設(shè)置href屬性,當(dāng)用戶點(diǎn)擊這個標(biāo)簽后,URL就會發(fā)生改變,也就會觸發(fā)hashchange事件了:

<a href="#srtian">srtian</a>

還有一種方式就是直接使用JavaScript來對loaction.hash進(jìn)行賦值,從而改變URL,觸發(fā)hashchange事件:

location.hash="#srtian"

2.2 基于History API

前面的hash雖然也很不錯,但使用時都需要加上#,并不是很美觀。因此到了HTML5,又提供了History API來實現(xiàn)URL的變化。其中做最主要的API有以下兩個:history.pushState()和history.repalceState()。

這兩個API可以在不進(jìn)行刷新的情況下,操作瀏覽器的歷史紀(jì)錄。唯一不同的是,前者是新增一個歷史記錄,后者是直接替換當(dāng)前的歷史記錄。此外,這兩個api都接受三個參數(shù):

window.history.pushState(null, null, "http://www.163.com");
  • 狀態(tài)對象(state object):一個JavaScript對象,與用pushState()方法創(chuàng)建的新歷史記錄條目關(guān)聯(lián)。無論何時用戶導(dǎo)航到新創(chuàng)建的狀態(tài),會觸發(fā)popstate事件,并能在事件中使用該對象。
  • 標(biāo)題(title):一般瀏覽器會忽略,最好傳入null。
  • 地址(URL):就是需要新增的歷史記錄的地址,瀏覽器不會去直接加載改地址,但后面也可能會去嘗試加載該地址。此外需要注意的是,傳入的URL與當(dāng)前URL應(yīng)該是同源的。

這一塊的詳情可以去看MDN,它做了一個比較詳細(xì)的介紹:

https://developer.mozilla.org/en-US/docs/Web/API/History

此外,還提供了popstate事件來監(jiān)聽歷史記錄的變化。

可以看看下面這個實例:

<p id="example">
 <a href="/name" title="name">name</a>
 <a href="/age" title="age">age</a>?
</p>
<div class="main" id="main"></div>
<script>
;(function(){
    var examplebox = document.getElementById('example')
    var mainbox = document.getElementById('main')
    
    examplebox.addEventListener('click', function(e){
        e.preventDefault()
        var elm = e.target
        var uri = elm.href
        var tlt = elm.title
        history.pushState({path:uri,title:tlt}, null, uri)
        mainbox.innerHTML = 'current page is '+tlt
    })
    window.addEventListener('popstate',function(e){
        var state = e.state
        mainbox.innerHTML = 'current page is ' + state.title
    })
})()
</script>

兩種實現(xiàn)方式的對比:基于Hash的路由實現(xiàn),兼容性更好;而基于History API的路由,則更正式,更美觀,可以設(shè)置與當(dāng)前URL同源的任意URL,路徑更直觀。此外,基于Hash的路由不需要對服務(wù)器做改動,基于History API的路由需要對服務(wù)器做一些改造,需要對不同的路由進(jìn)行相應(yīng)的設(shè)置才行。

三、React-Router

由于我現(xiàn)在也就對React-Router比較熟悉,Vue學(xué)了太久了,忘記了。Angular剛學(xué),還沒到路由這塊這來,因此就以React-Router為例,來聊聊現(xiàn)代前端框架的路由實現(xiàn)思路。先來看看一個簡單的React中的Router代碼:

<Router>
    <Switch>
        <Route path="/about" component={About}/> 
        <Route path="/:user" component={User}/> 
    </Switch> 
</Router>

其實現(xiàn)思路也很簡單如下圖所示:


image

其實現(xiàn)思路很簡單:About和User這兩個component一直都存在。當(dāng)路由發(fā)生改變時,與URL相匹配的component機(jī)會被成功渲染。而不匹配的component就設(shè)置為null。

具體實現(xiàn),大家可以看這篇文章,這位大佬做了很詳細(xì)的介紹:

https://github.com/youngwind/blog/issues/109

參考資料:

https://github.com/youngwind/blog/issues/109

https://zhuanlan.zhihu.com/p/24814675

https://segmentfault.com/a/1190000007238999

http://www.cnblogs.com/yuqing6/p/6731980.html

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

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

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