最近使用的 React + webpack 來開發項目,感受確實是爽的飛起,然而總感受仍是少了點什麼。
對,是多頁面,每次請求頁面還要後端路由給你?多不爽啊,試試 react-router ,簡直掌控一切的感受,只開放一個頁面路由接口,其餘全給數據接口就了,能夠和後端哥哥說拜拜了。
??????????? 啪啪啪啪啪~html
先貼上官方文檔 https://github.com/rackt/react-router/tree/master/docs.
對了這裏還有一份中文文檔(不過不是很全)http://react-guide.github.io/react-router-cn/.
react-router 是 React 的完整前端路由解決方案,特別在作一個 spa 應用的時候,他能實現 url 和 視圖ui 的同步,而且支持後端渲染,異步按需加載等等。
因爲 react-router 文檔的多變,這裏的例子以當前版本 1.0.1 爲準。(1.0以前文檔每個版本的變更都很大,索多了都是淚)前端
全部支持 React 的瀏覽器。
倡議!建議你們也勸說身邊的人用現代化瀏覽器,關愛前端開發者。react
npm install react-router@latest
同時,react-router 是基於 history 開發的,這裏你須要安裝 history。
注意 react-router 當前版本 1.0.1 依賴的是 history 1.13.1 請不要安裝最新版。
不要問我爲何知道,被坑慘了;有同窗問,那沒有辦法用 history 的最新版本嘛?畢竟這只是暫緩之計,解決方案仍是有的,那就是等 react-router 做者解決咯 ?。webpack
npm install history@1.13.1
構建工具的話,我依然建議是 webpack , React 和 webpack 是一對好兄弟。git
npm install webpack
webpack的使用方法能夠看個人前兩篇文章:es6
如何使用webpack—webpack-howto
(求贊!!!)web
// 加載依賴包,這是 es6 的語法(我好囉嗦)。 import React from 'react' import { render } from 'react-dom' // 這裏從 react-router 引入了三個組件,先不解釋。 import { Router, Route, Link } from 'react-router' const App = React.createClass({ render() { return ( <div> <h1>App</h1> <ul> <li><Link to="/about">About</Link></li> <li><Link to="/inbox">Inbox</Link></li> </ul> {this.props.children} </div> ) } }); const Inbox = React.createClass({ render() { return ( <div> Inbox </div> ) } }); const About = React.createClass({ render() { return ( <div> About </div> ) } }); render(( <Router> <Route path="/" component={App}> <Route path="about" component={About} /> <Route path="inbox" component={Inbox} /> </Route> </Router> ), document.getElementById('root'));
我就偷個懶把官方文檔的demo直接copy了。
直接先來看 render 下面的內容,這裏是用 jsx 語法。 最外層組件是 Router(能夠把他看做是react-router提供的最外層容器) , 下一層是 Route,這個是路由組件。對應關係以下:/ ---> <App />
/about ---> <App><About /></App>
/inbox ---> <App><Inbox /></App>
path 對應路徑,component 對應當前路徑渲染的組件。
Route 裏面的 Route 表示在父組件路由的 path 路徑下面的一級 path 對應的路由,這裏的路由是父子嵌套,對應的組件也一樣是父子嵌套的。
若是是多級嵌套也一樣如此。算法
以上例子能夠改寫成:npm
... const routerConfig = [ { path: '/', component: App, childrenRoutes: [ { path: 'about', component: About }, { path: 'inbox', component: Inbox }, ] } ]; render(( <Router routes={routeConfig} /> ), document.getElementById('root'));
這裏的結構就更清晰了,我是比較喜歡這種方式。
好比上面的例子,/ 對於的組件是 App , 若是 App 只是渲染了一個導航條,卻沒有自組件,那打開 好比 qiutc.me/ 的時候不是就沒有內容了嗎。
把上面例子改一下
... // 添加組件 Index const Index = React.createClass({ render() { return ( <div> Index Index Index </div> ) } }); // 修改配置 const routerConfig = [ { path: '/', component: App, indexRoute: { component: Index }, childrenRoutes: [ { path: 'about', component: About }, { path: 'inbox', component: Inbox }, ] } ];
這裏加了一個 indexRoute
,表示他在沒有匹配子路由的時候,在 / 路由下渲染默認的子組件 Index。
路由組件嵌套也是同樣的,好比:
{ path: 'about', component: About, indexRoute: {component: AboutIndex} },
以此類推。
若是咱們打開了一個沒有設置路由的連接,就必然須要一個友好的 404 頁面。配置以下:
... // 添加 404 組件 const NotFound = React.createClass({ render() { return ( <div> 404 NotFound </div> ) } }); // 修改配置 const routerConfig = [ { path: '/', component: App, indexRoute: { component: Index }, childrenRoutes: [ { path: 'about', component: About }, { path: 'inbox', component: Inbox }, ] }, { path: '*', component: NotFound, } ];
如此簡單。
const routerConfig = [ { path: '/', component: App, indexRoute: { component: Index }, childrenRoutes: [ { path: 'about', component: About }, { path: 'inbox', component: Inbox, childrenRoutes: [ { path: 'message/:id', component: Message, } ], }, ] }, { path: '*', component: NotFound, } ];
在這裏咱們訪問 /inbox/message/1 對於渲染 Message 組件,這個連接太長了,咱們想直接 /message/1 那怎麼辦,改路由結構?太麻煩了!絕對路徑能夠幫你作到這個。
把 path: 'message/:id',
改成 path: '/message/:id',
就行了。
等等若是用戶以前收藏的連接是 /inbox/message/1 ,那不是就打不開了嘛,和後端路由同樣,react-router 也有重定向:redirect
const routerConfig = [ { path: '/', component: App, indexRoute: { component: Index }, childrenRoutes: [ { path: 'about', component: About }, { path: 'inbox', component: Inbox, childrenRoutes: [ { path: '/message/:id', component: Message, }, { path: 'message/:id', onEnter: function (nextState, replaceState) { replaceState(null, '/messages/' + nextState.params.id); } } ], }, ] }, { path: '*', component: NotFound, } ];
function (nextState, replaceState) { replaceState(null, '/messages/' + nextState.params.id); }
nextState
表示要進入的下一個路由,這裏就是 /inbox/messages/:id ,replaceState
表示替換路由狀態的方法,把 /inbox/messages/:id 替換成 /messages/:id,而後就能夠重定向到 /messages/:id。
表示在離開路由前執行。
———————————————————
固然若是你用的是 jsx 語法,有更簡單的組件能夠實現:
import { Redirect } from 'react-router' React.render(( <Router> <Route path="/" component={App}> <IndexRoute component={Index} /> <Route path="about" component={About} /> <Route path="inbox" component={Inbox}> <Route path="/messages/:id" component={Message} /> {/* 跳轉 /inbox/messages/:id 到 /messages/:id */} <Redirect from="messages/:id" to="/messages/:id" /> </Route> </Route> </Router> ), document.getElementById('root'))
React Router 使用路由嵌套的概念來讓你定義 view 的嵌套集合,當一個給定的 URL 被調用時,整個集合中(命中的部分)都會被渲染。嵌套路由被描述成一種樹形結構。React Router 會深度優先遍歷整個理由配置來尋找一個與給定的 URL 相匹配的路由。
簡單來說,就是說,匹配的時候會先匹配到外層路徑,而後依次遍歷到內層。
好比 /inbox/messages/:id 會先匹配 /,渲染 / 對應的組件 App,而後再到 / 的下一層尋找 /inbox ,一樣渲染 /inbox 對應的組件 Inbox,依次類推,直到到 message/:id。
tip:使用絕對路徑能夠忽略嵌套關係,如上面例子。
路由路徑是匹配一個(或一部分)URL 的 一個字符串模式。大部分的路由路徑均可以直接按照字面量理解,除了如下幾個特殊的符號:
/:paramName – 匹配一段位於 /、? 或 # 以後的 URL。 命中的部分將被做爲一個參數
(/) – 在它內部的內容被認爲是可選的
/* – 匹配任意字符(非貪婪的)直到命中下一個字符或者整個 URL 的末尾,並建立一個 splat 參數
<Route path="/hello/:name"> // 匹配 /hello/michael 和 /hello/ryan <Route path="/hello(/:name)"> // 匹配 /hello, /hello/michael 和 /hello/ryan <Route path="/files/*.*"> // 匹配 /files/hello.jpg 和 /files/path/to/hello.jpg
最後,路由算法會根據定義的順序自頂向下匹配路由。所以,當你擁有兩個兄弟路由節點配置時,你必須確認前一個路由不會匹配後一個路由中的路徑。例如:
<Route path="/comments" ... /> <Redirect from="/comments" ... />
第二個是不會被執行的。
好比上面的 /messages/:id ,這個id多是咱們在 Message 獲取數據時須要的 id。
他會被當作一個參數傳給 params,parmas 會傳給 Message 組件的 props:
const Message = React.createClass({ render: function() { return ( <div>{ this.props.params.id }</div> ); } });
這樣就能夠獲取到了。
React Router 是創建在 history 之上的。 簡而言之,一個 history 知道如何去監聽瀏覽器地址欄的變化, 並解析這個 URL 轉化爲 location 對象, 而後 router 使用它匹配到路由,最後正確地渲染對應的組件。
經常使用的 history 有三種形式, 可是你也可使用 React Router 實現自定義的 history。
createHashHistory
createBrowserHistory
createMemoryHistory
這是一個你會獲取到的默認 history ,若是你不指定某個 history 。它用到的是 URL 中的 hash(#)部分去建立形如 example.com/#/some/path 的路由。
這個 支持 ie8+ 的瀏覽器,可是由於是 hash 值,因此不推薦使用。
Browser history 是由 React Router 建立瀏覽器應用推薦的 history。它使用 History API 在瀏覽器中被建立用於處理 URL,新建一個像這樣真實的 URL example.com/some/path。
不會在地址欄被操做或讀取。
import { createBrowserHistory, useBasename } from 'history'; const historyConfig = useBasename(createHistory)({ basename: '/' // 根目錄名 }); ... render(( <Router routes={routeConfig} History={historyConfig} /> ), document.getElementById('root'));
咱們在最開頭看到這樣一個東西:
const App = React.createClass({ render() { return ( <div> <h1>App</h1> <ul> <li><Link to="/about">About</Link></li> <li><Link to="/inbox">Inbox</Link></li> </ul> {this.props.children} </div> ) } });
Link 會被渲染成 a ,to 其實就是 href ,
可是 react-router 會阻止默認跳轉頁面,而改爲 history 路由的變換。
參數:
to
切換到的路由地址
query
跟在 url 的 query 參數,好比
query={{q: "que"}} 對應 `/example?a=que
這裏的 query 一樣能夠像 params 會被傳入下一個路由組件的 props
hash
跟在 url 的 hash 參數,好比
hash={111} 對應 `/example#111
這裏的 query 一樣能夠像 params 會被傳入下一個路由組件的 props
activeClassName
當前 url 路徑若是和 Link 的 to 匹配 這個 Link 就會有一個定義的屬性,好比:
在 /index 下 <Link to="/index" activeClassName={"active"} activeStyle={{color: 'red'}} >/</Link> 這裏渲染出來的 a 標籤會有一個激活的 active 類名,還會有一個顏色 red <Link to="/about" activeClassName={"active"} activeStyle={{color: 'red'}} >/</Link> 這裏渲染出來的 a 標籤就不會有以上屬性
activeStyle
同上
onClick
點擊的時候執行的函數,會傳入一個 e 事件對象,你能夠 e.stopPropagation()
阻止默認路由切換。
在上面有一個問題若是:
在 / 下 和 /index <Link to="/" activeClassName={"active"} activeStyle={{color: 'red'}} >/</Link> 這個 Link 渲染出來的 a 標籤都會激活 active 屬性,而且會帶上 color: 'red' 由於 / 和 /index 和 / 都是匹配的
這時候就能夠用:
<IndexLink to="/" activeClassName={"active"} activeStyle={{color: 'red'}} >/</IndexLink> 只會在 / 下唄激活,在 /index 或者其餘下面,不會被激活
關於 根據路由按需異步加載js 和 服務器端渲染路由視圖 以及 react-router的更高級用法 會在下一篇文章來探討。畢竟哥也須要去深刻研究一下才敢獻醜。?