上一篇react-router原理之Link跳轉中提到了Link在onClick的處理函數中會調用history的push(或replace)方法。接下來咱們就以push方法爲例來看一下history具體都作了些什麼。Link中的history是經過context傳入進來的,須要向外層進行查找,繼續以官網爲例,最外層是BrowserRouter。html
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
const BasicExample = () => (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
...
</ul>
<Route exact path="/" component={Home} />
...
</div>
</Router>
);
複製代碼
打開BrowserRouter文件,能夠看到聲明瞭實例屬性history對象,history對象的建立來自history包的createBrowserHistory方法。html5
import { createBrowserHistory as createHistory } from "history";
class BrowserRouter extends React.Component {
history = createHistory(this.props);
render() {
return <Router history={this.history} children={this.props.children} />;
}
}
複製代碼
createBrowserHistory(存儲在modules/createBrowserHistory.js)最後返回一個history對象,history對象上擁有許多的屬性和方法,其中就有push、replace、listen等。react
關於push方法核心代碼就兩行數組
globalHistory.pushState({ key, state }, null, href);
setState({ action, location });
複製代碼
globalHistory對應的瀏覽器環境中的window.history對象,雖然window能夠監聽popstate事件,可是執行pushState或者replaceState是不會觸發該事件的,只有點擊瀏覽器的前進後退按鈕時纔會觸發,所以調用pushState方法只是改變了地址欄的url,其餘的沒有任何變化。瀏覽器
爲了達到url變化即從新渲染頁面的目的,就須要用到setState方法了(這裏的setState方法只是一個普通的函數)bash
setState方法中最關鍵的就是下面這一行代碼,執行notifyListeners方法遍歷listeners數組中的每一個listener並調用執行。服務器
transitionManager.notifyListeners(history.location, history.action);
// notifyListeners方法定義
let listeners = [];
const notifyListeners = (...args) => {
listeners.forEach(listener => listener(...args));
};
複製代碼
若是把從新渲染頁面的邏輯加入到listeners數組中,那麼當點擊Link的時候就能夠實現頁面更新的目的了。接下來就須要回到history生成的地方也就是BrowserHistory去找一找添加listener的邏輯,BrowserRouter在建立好history對象以後,經過props的形式把history傳遞給了Router。react-router
Router針對history作了兩件事dom
componentWillMount() {
const { children, history } = this.props;
this.unlisten = history.listen(() => {
this.setState({
match: this.computeMatch(history.location.pathname)
});
});
}
複製代碼
Router組件是Route的父組件,因此當Router從新render的時候,那麼Route天然也能夠觸發render,這樣就能夠響應最新的url狀態了。函數
html5也提供了history方法,爲何react-router要用history包呢?
雖然history包的createBrowserHistory其實底層依賴的就是html5的history,不過history除了支持createBrowserHistory以外,還提供createHashHistory和createMemoryHistory,這三種方式底層依賴的基礎技術各不相同,可是對外暴露的接口都是一致的。這其實就是history包的意義所在
history包對環境的差別進行抽象,提供統一的一致性接口,輕鬆實現了會話的管理
react-router是支持服務器端渲染的,因爲在服務器環境中不存在html5的history對象,所以沒法使用history包,因此也不能使用BrowserRouter。
針對服務器端環境,react-router提供了StaticRouter,StaticRouter與BrowserRouter最大的區別就體如今建立的history對象上面,二者的history對象擁有幾乎徹底一致的屬性方法。因爲服務器環境沒有history,所以也不會有history的改變,所以StaticRouter的history的方法(push、replace、go)等都是不可調用執行的。