聊聊React Router中的History

在學習React Router時,看到有關History有以下描述:html

React Router 是創建在 history 之上的。 簡而言之,一個 history 知道如何去監聽瀏覽器地址欄的變化, 並解析這個 URL 轉化爲 location 對象, 而後 router 使用它匹配到路由,最後正確地渲染對應的組件。

這段描述看的暈暈的,history究竟是個什麼東西呢?這得從history API的出身提及。前端

history對象的誕生

咱們都知道一個URL表明了網絡上惟一的一個資源。這個資源能夠是一個頁面,一張圖片等等。在地址欄裏輸入一個url地址,瀏覽器就會將對應的資源展現出來。當在不一樣的地址之間跳轉時,咱們很天然地想要回退或者前進一個地址。爲了實現這個功能,瀏覽器廠商定義了history對象。這時的history對象大體有go()forward()back()等方法,用於實現歷史記錄的訪問、跳轉。html5

使用 history API與瀏覽器歷史記錄進行交互

ajax的挑戰

history 與 URL 就這樣和諧的相處了一段時間,直到ajax技術的興起。git

瀏覽器有一個限制:若是你改變了URL,甚至是經過腳本,它就得向服務器發送請求,刷新整個頁面。這很耗時,也耗資源,由於有時候2個頁面長得差很少,僅僅有一小塊地方不一樣。爲了解決這個問題,ajax技術興起,利用ajax,能夠不改變URL,只向服務器請求須要變更的數據,而後在前端經過DOM操做實現頁面的局部更新。github

ajax極大提升了頁面加載速度與用戶體驗。不過它同時帶來了一個問題:同一個URL地址,可能會展現不少不一樣的信息,那麼它做爲惟一資源標識符的這個定義,在這裏被破壞了。ajax

有沒有一個一箭雙鵰的辦法呢?既能夠實現頁面的局部更新,又可以改變地址欄裏的URL?瀏覽器

解決辦法

井號方案

第一個跳出來解決問題的是URL中的##表明網頁中的一個位置。它右邊的字符就是頁面中的位置標識符。例以下面的URL:服務器

http://www.example.com/index.html#print

就表明網頁index.html的print位置。瀏覽器讀取這個URL後,會自動將print位置滾動至對應的區域。爲網頁位置指定標識符,有兩個方法。一是使用錨點,好比,二是使用id屬性,好比<div id="print" >網絡

#的威力在於它是用來指導瀏覽器動做的,對服務器端徹底無用。因此,HTTP請求中不包括#,改變#不觸發網頁重載。所以,若是咱們在使用ajax局部刷新頁面時,同時改變URL#後面的標識符,就實現了局部刷新+改變URL了。學習

事實上,早前的twitter、google就是這麼實現的。google甚至爲此專門定義了一個搜索引擎優化的標識符#!。能夠參考這篇文章:URL的井號

history方案

URL畢竟是用於資源分享的,帶個#總感受有點彆扭。若是不用#,有沒有辦法可以修改URL,同時又不向服務器發送請求呢?這就輪到history對象再次登場了。在HTML5規範中,W3C對history對象進行了一波升級:

HTML5 history API包括2個方法:history.pushState()和history.replaceState(),以及1個事件:window.onpopstate。

利用這2個方法,能夠實現經過腳本修改瀏覽器中的URL地址,而不觸發頁面重載。採用這個方案,一個ajax請求過程是這樣的:

  1. 經過腳本發送ajax請求到服務器,獲取服務器響應數據
  2. 根據響應的數據操做DOM,更新頁面內容
  3. 利用history.pushState()或replaceState()方法,修改地址欄中的URL

問題獲得解決。

React Router的整合

在React Router中,將上述2個方案進行了整合,統一到了一個history庫中。也就是咱們看到的HashHistoryBrowserHistoryHashHistory是上面井號方案的封裝,BrowserHistory則是對History方案的封裝。特別須要注意到是,BrowserHistory須要對服務器端改造。

爲何?

考慮如下場景,咱們用React Router的BrowserHistory開發了一個單頁面應用,主頁地址以下:

http://www.mysite.com/index

在index中能夠點擊連接進入歡迎頁,詳細介紹頁等,地址爲:

http://www.mysite.com/index/welcome
http://www.mysite.com/index/detail

若是咱們在瀏覽器裏直接輸入主頁面地址,而後經過頁面裏連接跳轉到二級頁面,通常是沒有問題的。可是,若是在瀏覽器裏直接輸入http://www.mysite.com/index/welcome會發生什麼呢?

瀏覽器會認爲這是一個URL請求,向服務器端請求這個URL表明的資源。但咱們這是一個單頁面應用,服務端只有一個index.html頁面,沒有跟這個地址匹配的資源,頁面就會報錯了。

所以,咱們須要稍微改造一下服務端,將全部這樣的請求都指向index.html頁面,由前臺處理路由信息。

總結

#有話說:

爲嘛,長得醜就要被淘汰嗎!

參考資料

相關文章
相關標籤/搜索