基本知識
基本用法
React Router
安裝指令如下:
路由器
Router
就是
React
的一個元件。
import {Router} from 'react-router';
render(
<Router/>,
document.getElementById('app')
);
要注意的是:
Router
元件本身隻是一個容器,真正的路由要通過
Route
元件定義。
import { Router, Route, hashHistory } from 'react-router';
render((
<Router history={hashHistory}>
<Route path="/" component={App}/>
</Router>
),
document.getElementById('app'));
上面代碼: 當通路根路由/(比如http://www.example.com/),元件APP就會加載到
document.getElementById('app')
。
注意:
Router
元件有一個參數
history
。
它的值
hashHistory
表示:路由的切換由URL的hash變化決定,即URL的#部分發生變化。
舉例來說:當通路
http://www.example.com/
的時候,實際會看到的是
http://www.example.com/#/
。
Route
元件定義了URL路徑與元件的對應關系。你可以同時使用多個Route元件:
<Router history={hashHistory}>
<Route path="/" component={App}/>
<Route path="/repos" component={Repos}/>
<Route path="/about" component={About}/>
</Router>
上面代碼中:使用者通路/repos(比如http://localhost:8080/#/repos)時,加載Repos元件;通路/about(http://localhost:8080/#/about)時,加載About元件。
嵌套路由
Route元件還可以嵌套:
<Router history={hashHistory}>
<Route path="/" component={App}>
<Route path="/repos" component={Repos}/>
<Route path="/about" component={About}/>
</Route>
</Router>
上面代碼中:使用者通路/repos時,會先加載App元件,然後在它的内部再加載Repos元件。
<App>
<Repos/>
</App>
App元件要寫成下面的樣子:
export default class App extends React.Component{
render() {
return (
<div>
<h1>React Router Tutorial</h1>
<ul role="nav">
<li>
<Link to="/about" style={{color: 'red'}}>About</Link>
</li>
<li>
<Link to="/repos" style={{color: 'green'}}>Repos</Link>
</li>
</ul>
{this.props.children}
</div>
)
}
}
上面代碼中,App元件的
this.props.children
屬性就是子元件。
path 屬性
Route
元件的
path
屬性指定路由的比對規則,這個屬性是可以省略的。如果省略,不管路徑是否比對,總是會加載指定元件。
<Route path="inbox" component={Inbox}>
<Route path="messages/:id" component={Message} />
</Route>
上面代碼中,當使用者通路
/inbox/messages/:id
時,會加載下面的元件。
<Inbox>
<Message/>
</Inbox>
如果省略外層Route的path參數,寫成下面的樣子。
<Route component={Inbox}>
<Route path="inbox/messages/:id" component={Message} />
</Route>
現在使用者通路
/inbox/messages/:id
時,元件加載還是原來的樣子。
<Inbox>
<Message/>
</Inbox>
通配符
path屬性可以使用通配符。
<Route path="/hello/:name">
// 比對 /hello/michael
// 比對 /hello/ryan
<Route path="/hello(/:name)">
// 比對 /hello
// 比對 /hello/michael
// 比對 /hello/ryan
<Route path="/files/*.*">
// 比對 /files/hello.jpg
// 比對 /files/hello.html
<Route path="/files/*">
// 比對 /files/
// 比對 /files/a
// 比對 /files/a/b
<Route path="/**/*.jpg">
// 比對 /files/hello.jpg
// 比對 /files/path/to/file.jpg
通配符的規則如下:
-
:paramName
:paramName比對URL的一個部分,直到遇到下一個/、?、#為止。這個路徑參數可以通過this.props.params.paramName取出。
-
()
()表示URL的這個部分是可選的。
-
*
*比對任意字元,直到模式裡面的下一個字元為止。比對方式是非貪婪模式。
-
**
** 比對任意字元,直到下一個/、?、#為止。比對方式是貪婪模式。
path屬性也可以使用相對路徑(不以/開頭),比對時就會相對于父元件的路徑。嵌套路由如果想擺脫這個規則,可以使用絕對路由。
特别注意: 路由比對規則是從上到下執行,一旦發現比對,就不再其餘的規則了。
<Route path="/comments" ... />
<Route path="/comments" ... />
上面代碼中,路徑
/comments
同時比對兩個規則,第二個規則不會生效。
設定路徑參數時,需要特别小心這一點。
<Router>
<Route path="/:userName/:id" component={UserPage}/>
<Route path="/about/me" component={About}/>
</Router>
上面代碼中,使用者通路
/about/me
時,不會觸發第二個路由規則,因為它會比對
/:userName/:id
這個規則。是以,帶參數的路徑一般要寫在路由規則的底部。
對于
URL
的查詢字元串
/foo?bar=baz
,可以用
this.props.location.query.bar
擷取。
Link标簽
Link
元件用于取代
<a>
元素,生成一個連結,允許使用者點選後跳轉到另一個路由。它基本上就是
<a>
元素的React 版本,可以接收
Router
的狀态。
render() {
return <div>
<ul role="nav">
<li><Link to="/about">About</Link></li>
<li><Link to="/repos">Repos</Link></li>
</ul>
</div>
}
特别注意:Link 元件中的to屬性定義了 URL 中 #号 之後的路徑參數,是以要和 Route 元件中的 path值相對應。
注意: 在Router元件之外,導航到路由頁面,可以使用浏覽器的History API,像下面這樣寫:
import { browserHistory } from 'react-router';
browserHistory.push('/some/path');
IndexRoute 元件
看下面的例子更好了解:
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route path="/repos" component={Repos}/>
<Route path="/about" component={About}/>
</Route>
</Router>
對于上面的代碼,如果我們通路根路徑/,不會加載任何子元件。App元件的
this.props.children
是
null
。
IndexRoute
就是用來顯式指定根路由的子元件,即指定預設情況下加載的子元件。你可以把IndexRoute想象成某個路徑的index.html。
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={Home}/>
<Route path="/repos" component={Repos}/>
<Route path="/about" component={About}/>
</Route>
</Router>
現在,使用者通路/的時候,加載的元件結構如下:
<App>
<Home/>
</App>
這種元件結構就很清晰了:App隻包含下級元件的共有元素,本身的展示内容則由Home元件定義。這樣有利于代碼分離,也有利于使用React Router提供的各種API。
特别注意:
IndexRoute
元件沒有路徑參數
path
。
Redirect 元件
<Redirect>
元件用于路由的跳轉,即使用者通路一個路由,會自動跳轉到另一個路由。
<Route path="inbox" component={Inbox}>
{/* 從 /inbox/messages/:id 跳轉到 /messages/:id */}
<Redirect from="messages/:id" to="/messages/:id" />
</Route>
現在通路
/inbox/messages/5
,會自動跳轉到
/messages/5
。
IndexRedirect 元件
IndexRedirect
元件用于通路根路由的時候,将使用者重定向到某個子元件。
<Route path="/" component={App}>
<IndexRedirect to="/welcome" />
<Route path="welcome" component={Welcome} />
<Route path="about" component={About} />
</Route>
上面代碼中,使用者通路根路徑時,将自動重定向到子元件
welcome
。
IndexLink
如果連結到根路由/,不要使用Link元件,而要使用
IndexLink
元件。
這是因為對于根路由來說,
activeStyle和activeClassName
會失效。或者說總是生效,因為/會比對任何子路由。而IndexLink元件會使用路徑的精确比對。
<IndexLink to="/" activeClassName="active">
Home
</IndexLink>
上面代碼中,根路由隻會在精确比對時,才具有
activeClassName
。
另一種方法是使用Link元件的
onlyActiveOnIndex
屬性,也能達到同樣效果。
<Link to="/" activeClassName="active" onlyActiveOnIndex={true}>
Home
</Link>
實際上,
IndexLink
就是對Link元件的
onlyActiveOnIndex
屬性的包裝。
histroy 屬性
Router
元件的
history
屬性,用來監聽浏覽器位址欄的變化,并将URL解析成一個位址對象,供
React Router
比對。
history屬性,一共可以設定三種值:
-
browserHistory
如果設為
,浏覽器的路由就不再通過Hash完成了,而顯示正常的路徑browserHistory
,背後調用的是浏覽器的example.com/some/path
History API
。
但是,這種情況需要對
伺服器改造
。否則使用者直接向伺服器請求某個子路由,會顯示網頁找不到的404錯誤。
如果開發伺服器使用的是
參數就可以了。webpack-dev-server,加上--history-api-fallback
-
hashHistory
如果設為hashHistory,路由将通過URL的hash部分(#)切換,URL的形式類似
example.com/#/some/path
- createMemoryHistory
主要用于伺服器渲染。它建立一個記憶體中的history對象,不與浏覽器URL互動。createMemoryHistory
react-router 是基于 history 子產品提供的 api 進行開發的。
假如我們有一台提供 Web 服務的伺服器的網絡位址是:10.0.0.1,而該 Web 服務又提供了三個可供使用者通路的頁面,其頁面 URI 分别是:
http://10.0.0.1/
http://10.0.0.1/about
http://10.0.0.1/concat
那麼其路徑就分别是 /,/about,/concat。
當使用者使用 http://10.0.0.1/about 來通路該頁面時,Web 服務會接收到這個請求,然後會解析 URI 中的路徑 /about,在 Web 服務的程式中,該路徑對應着相應的處理邏輯,程式會把請求交給路徑所對應的處理邏輯,這樣就完成了一次「路由分發」,這個分發就是通過「路由」來完成的。
參考博文:
React Router 使用教程
前端路由實作與 react-router 源碼分析