React Router 是專爲 React 設計的路由解決方案,在使用 React 來開發 SPA (單頁應用)項目時,都會須要路由功能,而 React Router 應該是目前使用率最高的。html
React Router 並非 Facebook 的 React 官方團隊開發的,可是聽說有官方人員參與開發。React Router 的設計思想來源於 Ember 的路由,若是原來有用過 Ember 的路由,那麼應該對 React Router 不會陌生。前端
對於沒有開發事後端,也沒有開發過 SPA 的前端來講,「路由」這個名詞可能會讓人比較困惑,這裏的路由並非指「硬件路由」,也不是網絡七層協議中的「網絡層路由」,可是其思想原理是同樣的。我儘可能簡單通俗的介紹一下。react
假如咱們有一臺提供 Web 服務的服務器的網絡地址是:10.0.0.1
,而該 Web 服務又提供了三個可供用戶訪問的頁面,其頁面 URI 分別是:git
那麼其路徑就分別是 /
,/about
,/concat
。github
當用戶使用 http://10.0.0.1/about
來訪問該頁面時,Web 服務會接收到這個請求,而後會解析 URI 中的路徑 /about
,在 Web 服務的程序中,該路徑對應着相應的處理邏輯,程序會把請求交給路徑所對應的處理邏輯,這樣就完成了一次「路由分發」,這個分發就是經過「路由」來完成的。後端
前端的路由和後端的路由在實現技術上不同,可是原理都是同樣的。在 HTML5 的 history
API 出現以前,前端的路由都是經過 hash
來實現的,hash
能兼容低版本的瀏覽器。若是咱們把上面例子中提到的 3 個頁面用 hash
來實現的話,它的 URI 規則中須要帶上 #
。瀏覽器
Web 服務並不會解析 hash
,也就是說 #
後的內容 Web 服務都會自動忽略,可是 JavaScript 是能夠經過 window.location.hash
讀取到的,讀取到路徑加以解析以後就能夠響應不一樣路徑的邏輯處理。服務器
history 是 HTML5 纔有的新 API,能夠用來操做瀏覽器的 session history (會話歷史)。基於 history
來實現的路由能夠和最初的例子中提到的路徑規則同樣。網絡
用戶可能都察覺不到該訪問地址是 Web 服務實現的路由仍是前端實現的路由。session
從性能和用戶體驗的層面來比較的話,後端路由每次訪問一個新頁面的時候都要向服務器發送請求,而後服務器再響應請求,這個過程確定會有延遲。而前端路由在訪問一個新頁面的時候僅僅是變換了一下路徑而已,沒有了網絡延遲,對於用戶體驗來講會有至關大的提高。
說了這麼多的「路由基礎」,該回頭來講說 React Router 了。
使用 React Router 來配置上面例子中的三個頁面,每一個頁面分別對應着一個 React Component。
/about
頁面的入口文件 about.js
import React from 'react';
class About extends React.Component {
componentDidMount () {
console.log('mount');
}
componentWillUnmount () {
console.log('un mount');
}
render () {
return (
<h3>This is About page.</h3>
);
}
};
export default About;
/concat
頁面的入口文件 concat.js
import React from 'react';
const Concat = () => {
return (
<h3>This is Concat page.</h3>
);
};
export default Concat;
/
首頁對應的是 app.js,它也是整個 React Component 的入口文件。
import React from 'react';
import {Link} from 'react-router';
class App extends React.Component {
render () {
return (
<div>
<h1>React Router Demo</h1>
<hr />
<p>
by <a href="http://stylechen.com/" target="_blank">stylechen.com</a>
</p>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/concat">Concat</Link></li>
<li><Link to="/list/001">List 001</Link></li>
<li><Link to="/list/002">List 002</Link></li>
</ul>
{this.props.children}
</div>
)
}
};
export default App;
app.js 中的 Link
組件是 React Router 提供的組件,用於連接到相應頁面。若是直接使用 a 標籤的話至關於頁面跳轉了,而使用 Link
只是應用內的路由跳轉,頁面跳轉意味着先從新加載整個頁面,而後纔是應用內部的路由跳轉。
index.js 中包含了 route 的配置,同時在該文件中對 React Component 進行 render
。
import React from 'react';
import ReactDOM from 'react-dom';
import App from './component/app';
import About from './component/about';
import Concat from './component/concat';
import List from './component/list';
import {Router, Route, browserHistory} from 'react-router';
const router = (
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route path="about" component={About} />
<Route path="concat" component={Concat} />
<Route path="list/:id" component={List} />
</Route>
</Router>
);
ReactDOM.render(
router,
document.getElementById('root')
);
Route
組件就是用於配置路由,path
屬性用於配置路徑,component
就是對應的 React Component。
Route
組件支持嵌套,嵌套時子組件的路徑能夠繼承父組件的路徑,上面的 about
嵌套後就成了 /about
,固然也能夠直接以根路徑爲開頭。
此時再看看 app.js 中的 this.props.children
,About
和 Concat
兩個頁面組件其實就是以這種形式插入到父組件中,只是插入的時候用 React Router 提供的組件再包裝了一下使它們能夠支持路由。
Router
組件還有一個重要的屬性,那就是history
,這能夠配置使用history
來實現路由,若是沒有配置這個屬性則默認使用hash
。
假如咱們有不少 list 頁面,這些頁面除了動態內容不一樣,其餘的頁面部分都相同,這個時候須要怎麼配置路由和組件呢?
這種場景就須要用到路由的參數功能,增長一條包含參數的路由配置。
注意 path
屬性中的 :id
就是該路由的參數(param
)。再來看看 List
頁面的組件。
/list
對應了 list.js
import React from 'react';
class List extends React.Component {
render () {
return (
<div>
<h3>This is List page.</h3>
<p>The list page id is
<b style={{color: 'red'}}>{this.props.params.id}</b>
</p>
</div>
);
}
};
export default List;
在 List
組件中,能夠直接經過 this.props.params.id
來訪問實際的參數值(這裏的id key 就和定義路徑的 :id
相對應),React Router 將路由的數據都經過 props
傳遞給了頁面組件,這樣就能夠很是方便的訪問路由相關的數據了。
每一個 React Component 都有生命週期,按照常規的策略,當調用父組件的 render
的時候,會將全部的頁面子組件也進行 render
,這種邏輯顯然不合理了。那麼當一個 React 的應用有了路由功能後,它的生命週期會如何處理呢?
當切換路由的路徑的時候纔去 render 對應的頁面組件,給 About
綁定兩個組件裝載和卸載的事件,就能夠測試出來了。
importReactfrom'react';