react router更新迭代很快,可是總感受一切事物都萬變不離其宗,很好的掌握了history這個對象,能在工做的時候更駕輕就熟。翻譯了一篇文章,若是有什麼不通之處但願指出。html
原文react
想要很好理解react router
,首先必需要學會history
。進一步講,history
包提供了react router
須要的核心功能。它使得單頁面應用能容易的根據導航改變location
。web
npm install –save history
複製代碼
history
包導出了三個方法,分別對應三種類型的history:browser
,hash
,memory
。typescript
import {
createBrowserHistory,
createHashHistory,
createMemoryHistory
} from 'history'
複製代碼
React router
自動幫咱們建立了history
對象,因此咱們沒必要本身去直接操做history。可是,理解它們可讓咱們知道其中哪一個更適合咱們的項目。shell
不管你建立什麼類型的history,最後都將獲得一個擁有相同屬性和方法的對象。npm
location
是history
中最重要的屬性,代表了應用當前'在哪',它包含了許多衍生自URL
的屬性,好比pathname
,search
,hash
。json
而且,每一個location對象都具備一個單一的key屬性用來區分當前location。react-native
最後,location還包含一個state屬性提供URL沒有展現的附加數據,即相關參數變量。數組
{
pathname: '/here',
search: '?key=value',
hash: '#extra-information',
state: { modal: true },
key: 'abc123'
}
複製代碼
首先須要一個原始的location
來用於建立history
對象,每種類型的history的原始location
不一樣,例如,browser history
會解析當前URL
。瀏覽器
雖然咱們每次只能訪問一個位置,可是history
會追蹤維護每一個訪問過的location
。正是這種可增長location
而且可在整個location
中穿梭的能力使得history
成爲"history"。若是history
僅僅關心當前location
,那麼它就要更名字叫"present"了。
除了location數組,history
還維護了一個當前location
關聯到數組中的索引值。 在memory history
中,這些都已經被定義好了,browser
和hash
histories中,location數組和索引值是被browser
控制的,不能直接被操做。
僅僅是包含一個location
的屬性遠遠不夠。navigation
方法才真正讓history
變得有趣起來。這些方法能夠操控改變當前的location
。
push
方法容許咱們定位到一個新的location
。它將在location
數組尾部添加一個新的location
。當調用push的時候,"future" locations(由於使用後退按鈕生成的當前定位location
後面的locations)就會被捨棄。 當你點擊Link的時候,默認使用的就是history.push
方法。
history.push({ pathname: '/new-place' })
複製代碼
和push方法類似,可是它會從數組中替換掉當前location,"Future" locations不會被刪除。React router
的<Redirect>
就應用了replace
。
例如,從當前頁面點擊連接跳轉到第二頁面,第二頁面會從新定向到第三個頁面。若是使用push
方法,那麼在第三頁面點擊後退按鈕的時候,會返回到第二頁面,而後又從新跳轉到第三頁面,循環往復。可是若是使用replace
,那麼後退返回的就是第一頁面。
history.replace({ pathname: '/go-here-instead' })
複製代碼
goBack
返回某一頁面,其實是減少了locations數組中的定位索引。
history.goBack()
複製代碼
goForward
與之相反,向前跳一個頁面。僅僅當** future locations **存在時纔會起做用。
history.goForward()
複製代碼
go
是二者的結合體,更增強大。負數參數用來後退,正數參數用來向前。
history.go(-3)
複製代碼
history
使用觀察者模式,外部代碼能夠監聽location
的變化。
每一個history對象都有listen
方法,接收一個方法做爲參數。這個方法將被加到history維護的監聽函數序列中。任什麼時候候location改變(不管是代碼控制改變仍是用戶點擊瀏覽器按鈕),history對象都會調用全部的監聽函數。這樣在location改變時就能夠作相關代碼操做。
const youAreHere = document.getElementById('youAreHere')
history.listen(function(location) {
youAreHere.textContent = location.pathname
})
複製代碼
React Router
的路由組件會監聽本身的history
對象,這樣當location
改變的時候就能夠re-render
了。
每一個history都有個createHref
方法,接收location
對象轉化成URL
。 例如<a>元素並不能正確解析history
對象,也沒法理解什麼是location
。因此爲了讓HTML在沒法解析history的狀況下正確導航,咱們須要將其轉化爲真正的URLs。
const location = {
pathname: '/one-fish',
search: '?two=fish',
hash: '#red-fish-blue-fish'
}
const url = history.createHref(location)
const link = document.createElement('a')
a.href = url
// <a href='/one-fish?two=fish#red-fish-blue-fish'></a>
複製代碼
以上覆蓋了history API的必須方法。還有更多的屬性和方法,可是上述已經足夠咱們理解history如何工做了。
三種類型的history仍是有些不一樣的,在使用過程紅須要咱們本身去斟酌到底什麼類型的才比較適合咱們的項目。下面對比不一樣的使用場景。
browser和hash histories通常應用於瀏覽器。它們與history
和location
web API交互從而保證當前瀏覽器地址欄展現的地址和當前location
一致。
const browserHistory = createBrowserHistory()
const hashHistory = createHashHistory()
複製代碼
上述兩種history最大的區別是根據URL建立location
的方式。browser history
會根據完整URL建立,可是hash
根據hash符號#
後面的部分URL建立。
// 以下 URL
url = 'http://www.example.com/this/is/the/path?key=value#hash'
// 'browser history' 建立的 location object:
{
pathname: '/this/is/the/path',
search: '?key=value',
hash: '#hash'
}
// 'hash history' 建立的 location object:
{
pathname: 'hash',
search: '',
hash: ''
}
複製代碼
既然這樣,爲何還會有人用hash history呢?
當你導航到一個URL的時候,理論上服務器會有一個對那個的文件被訪問。
對於動態服務器來說,被訪問文件不必定真的存在。服務器會檢測訪問URL並決定返回什麼HTML。可是對於靜態文件服務器來說,它僅僅能返回磁盤已存在的文件。它們能作的就是提供URL的時候返回這些匹配的index.html
文件。
鑑於靜態服務器的侷限性,最簡單粗暴的解決方式就是被訪問服務器具備'real' location
來對應訪問的HTML。固然,對於咱們的應用來說一個location對應一個URL,這樣history的使用將會毫無心義。爲了突破這種侷限性,hash history
使用URL的hash部分設置讀取location。
//假如example.com 使用靜態服務器, 這些URLs訪問的都是/my-site/index.html
http://www.example.com/my-site#/one
http://www.example.com/my-site#/two
// 可是, 若是使用hash history, 應用的URL就會變得不一樣,URL的hash部分會派生出不一樣的URLs。
{ pathname: '/one' }
{ pathname: '/two' }
複製代碼
基於hash的特性,它最大的用途就是當咱們使用靜態服務器的時候用於獲取HTML。
Memeory
最大的好處就是任何能用js
的地方均可以使用它。
例如能夠在Node環境的單元測試中使用它。這樣能夠直接依賴history對象測試代碼而不須要在瀏覽器中運行。
而且,它也能夠應用於手機端。被react-router-native
調用的memory history是基於react-native
應用的導航來實現location功能的。
甚至它也能夠應用在瀏覽器中(可是地址欄會丟失相應的地址信息)。
它和前二者者最大的不一樣是它維護的是自身內部的location數組。memory history建立之初就能夠設置原始數據,這些數據包含了location數組和當前location索引,而沒必要像其餘兩者同樣依賴瀏覽器存儲的locations。
const history = createMemoryHistory({
initialEntries: ['/', '/next', '/last'],
initialIndex: 0
})
複製代碼
本身實現history功能的時候,會遇到很多瀏覽器操做導航的坑,最簡單的方法就是依賴現成的history來解決問題。 不管用哪一種類型的history,最終目的都是爲了更好的實現應用中的導航功能。