前言html
第一次接觸這個是當年Twitter的改版,在訪問頁面路徑中出現/!#類這樣的結構。若是你的項目涉及到單頁面的話,路由是必不可少的。今日早讀文章由@陶自然受權分享。前端
正文從這開始~html5
什麼是前端路由後端
路由,引導、指路之意。api
譬如咱們熟知的路由器,蹦躂在網絡層的數據包轉發設備,在網絡中也是扮演着指路明燈的角色,肩負着將數據包正確導向目的地址的重任。瀏覽器
前端路由也借用了這個詞,可是承擔的工做全然不一樣,它是服務於客戶端瀏覽器的指路人。安全
所謂的前端路由,擁有這樣一種能力:客戶端瀏覽器能夠不依賴服務端,根據不一樣的URL渲染不一樣的視圖頁面。服務器
前端路由的存在合理性網絡
在Ajax之劍還未亮出,前端仍處於襁褓之中的時候,路由的工做交給了後端。在進行頁面切換的時候,瀏覽器發送不一樣的url請求;服務器接收到瀏覽器的請求時,經過解析不一樣的url去拼接須要的html或者模板,而後將結果返回給瀏覽器端進行渲染。併發
服務器端路由也是不落俗套的有利亦有弊。它的好處是安全性更高,更嚴格得控制頁面的展示。這在某些場景中是頗有用的,譬以下單支付流程,每一步只有在上一步成功執行以後才能抵達。這在服務器端能夠爲每一步流程添加驗證機制,只有驗證經過才返回正確的頁面。那麼前端路由不能實現每一步的驗證?天然不是,姑且相信你的代碼能夠寫的很嚴謹,保證正常狀況下流程不會錯,可是另外一個不得不面對的事實是:前端是毫無安全性可言的。用戶能夠肆意修改代碼來進入不一樣的流程,你可能會爲此添加很多的處理邏輯。相較之下,固然是後端控制頁面的進入權限更爲安全和簡便。
另外一方面,後端路由無疑增長了服務器端的負荷,而且須要reload頁面,用戶體驗其實不佳。
這樣,前端路由就有用武之地了。首先,它的出現無疑減輕了服務器端的壓力。特別是對於一個比較複雜的應用來說,或者更確切的說,對於擁有一個複雜路由系統的應用來講,服務器端須要爲每個不一樣的url執行一段處理邏輯在高併發的狀況下實在有點不堪重負;其次,頁面的切換能夠不須要刷新整個頁面了,沒有網絡延遲,沒有閃爍刷新,提高了用戶體驗。
前端路由實現方式
既然目標實現,咱們須要解決的問題有哪些?咱們能夠將問題拆的稍微細一點,先制定一個億的小計劃,實現以後再進行下一步:)
在頁面不刷新的前提下實現url變化
捕捉到url的變化,以便執行頁面替換邏輯
如何實現更新url而且頁面不刷新
正如前面所說,前端路由相較於後端路由的一個特色就是頁面在不徹底刷新的狀況下進行視圖的切換。頁面url變了,可是並無從新加載!看上去彷佛有點難以想象,其實也沒什麼大不了。
試想將瀏覽器地址欄當作一個輸入框,咱們須要實現的就是改變輸入框的value可是不觸發請求頁面的操做,這樣就不會從新加載新頁面。假若輸入框的值的變化和發送請求是一個原子操做,咱們也就一籌莫展了。慶幸的是,只有當咱們敲擊了回車以後,請求才會被髮送出去(這是顯而易見的吧)。所以這就爲咱們修改地址欄的值而不觸發頁面請求刷新創造了條件。BOM是否有提供修改瀏覽器地址欄url而不觸發請求操做的方法呢?
這裏,存在兩種知足需求的方式。一是利用url中的hash字段;二是使用html5提供的history API。
hash方式
瞭解http協議就會知道,url的組成部分有不少,譬如協議、主機名、資源路徑、查詢字段等等,其中包含一個稱之爲片斷的部分,以「#」爲標識。
例如: http://www.gmail.com/text/#123,123即是url中的hash部分。
打開控制檯,輸入 location.hash,你能夠獲得當前url的hash部分(若是當前url不存在hash則返回空字符串)。接下來,輸入 location.hash = '123',會發現瀏覽器地址欄的url變了,末尾增長了’#123’字段,而且,頁面沒有被從新刷新。很顯然,這很符合咱們的要求。
history API
html5引入了一個history對象,包含了一套訪問瀏覽器歷史的api,能夠經過window.history訪問到它。
這裏咱們看上了它的兩個api方法:pushState 和 replaceState。
若上所示,它們接收徹底相同的參數,都是對瀏覽器的歷史棧進行操做,將傳遞的url和相關數據壓棧,並將瀏覽器地址欄的url替換成傳入的url且不刷新頁面(正中下懷!)。
By the way,不一樣的地方是pushState 將指定的url直接壓入歷史記錄棧頂,而 replaceState 是將當前歷史記錄棧頂替換成傳入的數據。
這兩種方式均可以幫咱們知足題設條件。採用哪種方式除了主觀喜愛以外,還得依照客觀事實:低版本的瀏覽器對於history API的兼容性很差,例如遇到了IE8,擺在眼前的道路彷佛就別無選擇了。
如何跟蹤url變化
在瀏覽器端,跟蹤表單屬性的變化通常都採用事件監聽機制,跟蹤url的變化也不落俗套。
對於hash方式的前端路由,一般能夠監聽 hashchange 事件,在事件回調中處理相應的頁面視圖展現等邏輯。
此外,html5提供的 popstate 事件也會在url的hash發生改變時觸發。也就是說若是能夠忽略低版本瀏覽器,咱們使用hash方式路由時也能夠採用監聽這個事件進行回調處理。
那麼,若是是採用history API的形式呢?根據MDN的描述:
調用 history.pushState() 或者 history.replaceState() 不會觸發 popstate 事件。popstate 事件只會在瀏覽器某些行爲下觸發, 好比點擊後退按鈕(或者在JavaScript中調用 history.back() 方法)。
這也就是說,咱們在使用history API改變瀏覽器的url時,仍須要額外的步驟去觸發 popstate 事件,例如調用 history.back() 會 history.forward() 等方法。
從兼容性上來說,前面有說起hash的方式兼容性更好。然而,對於低版本的瀏覽器,例如IE6等等,不支持 hashchange 事件。這個時候咱們只能經過 setInterval 設置心跳的方式去模擬 hashchange。
一個簡單實現
這裏,給出一個很簡單的實現:
router.js
index.html
index.js
一點總結
應用場景
前端路由大部分的應用場景,就是咱們如今熟知的單頁應用SPA。
不存在純前端路由
咱們此前所描述的前端路由,創建在已經打開了一個初始頁面基礎之上,而後在這個頁面以內進行頁面替換。然而,咱們如何進入這個初始頁面?僅靠前端路由確定是力所不及。咱們至少要向後端發送一次http請求,接收所須要加載的頁面不是嗎?
因此,咱們並不能拋棄後端路由部分。這也意味着,咱們須要和後端確認各自的分工,哪些url歸前端解析,哪些歸後臺解析。
關於本文
做者:@陶自然
原文:http://tomasran.space/archives/1bjDjwd2FgzJIsCyshbzag/
感謝前端早讀課公衆號推薦此文