談談history

react router更新迭代很快,可是總感受一切事物都萬變不離其宗,很好的掌握了history這個對象,能在工做的時候更駕輕就熟。翻譯了一篇文章,若是有什麼不通之處但願指出。html

原文react

想要很好理解react router,首先必需要學會history。進一步講,history包提供了react router須要的核心功能。它使得單頁面應用能容易的根據導航改變locationweb

npm install –save history
複製代碼

history包導出了三個方法,分別對應三種類型的history:browserhashmemorytypescript

import {
    createBrowserHistory,
    createHashHistory,
    createMemoryHistory
} from 'history'
複製代碼

React router自動幫咱們建立了history對象,因此咱們沒必要本身去直接操做history。可是,理解它們可讓咱們知道其中哪一個更適合咱們的項目。shell

What is history?(什麼是history)

不管你建立什麼類型的history,最後都將獲得一個擁有相同屬性和方法的對象npm

Location

locationhistory中最重要的屬性,代表了應用當前'在哪',它包含了許多衍生自URL的屬性,好比pathnamesearchhashjson

而且,每一個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瀏覽器

One location to rule them all?(僅僅一個location來規範全部的功能麼?)

雖然咱們每次只能訪問一個位置,可是history會追蹤維護每一個訪問過的location。正是這種可增長location而且可在整個location中穿梭的能力使得history成爲"history"。若是history僅僅關心當前location,那麼它就要更名字叫"present"了。

除了location數組,history還維護了一個當前location關聯到數組中的索引值。 在memory history中,這些都已經被定義好了,browserhash histories中,location數組和索引值是被browser控制的,不能直接被操做。

Navigation

僅僅是包含一個location的屬性遠遠不夠。navigation方法才真正讓history變得有趣起來。這些方法能夠操控改變當前的location

Push

push方法容許咱們定位到一個新的location。它將在location數組尾部添加一個新的location。當調用push的時候,"future" locations(由於使用後退按鈕生成的當前定位location後面的locations)就會被捨棄。 當你點擊Link的時候,默認使用的就是history.push方法。

history.push({ pathname: '/new-place' })
複製代碼

Replace

和push方法類似,可是它會從數組中替換掉當前location,"Future" locations不會被刪除。React router<Redirect>就應用了replace

例如,從當前頁面點擊連接跳轉到第二頁面,第二頁面會從新定向到第三個頁面。若是使用push方法,那麼在第三頁面點擊後退按鈕的時候,會返回到第二頁面,而後又從新跳轉到第三頁面,循環往復。可是若是使用replace,那麼後退返回的就是第一頁面。

history.replace({ pathname: '/go-here-instead' })
複製代碼

Go, goBack, goForward

goBack 返回某一頁面,其實是減少了locations數組中的定位索引。

history.goBack()
複製代碼

goForward與之相反,向前跳一個頁面。僅僅當** future locations **存在時纔會起做用。

history.goForward()
複製代碼

go是二者的結合體,更增強大。負數參數用來後退,正數參數用來向前。

history.go(-3)
複製代碼

Listen

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了。

Linking things together

每一個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如何工做了。

With our powers combined

三種類型的history仍是有些不一樣的,在使用過程紅須要咱們本身去斟酌到底什麼類型的才比較適合咱們的項目。下面對比不一樣的使用場景。

In the Browser

browser和hash histories通常應用於瀏覽器。它們與historylocationweb 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: ''
}
複製代碼

Hashing things out

既然這樣,爲何還會有人用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。

Memory: The Catch-all History

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,最終目的都是爲了更好的實現應用中的導航功能。

相關文章
相關標籤/搜索