本教程引用馬倫老師的的教程css
React項目的可用的路由庫是React-Router
,固然這也是官方支持的。它也分爲:html
如下教程咱們都以Web端爲主,因此全部的教程內容都是默認關於
react-router-dom
的介紹。node
進行網站(將會運行在瀏覽器環境中)構建,咱們應當安裝react-router-dom
。react-router-dom
暴露出react-router
中暴露的對象與方法,所以你只須要安裝並引用react-router-dom
便可。react
官方文檔地址: https://reacttraining.com/react-router/web/guides/philosophygit
安裝:github
yarn add react-router-dom
# 或者,不使用 yarn
npm install react-router-dom
如今的React Router版本中已不須要路由配置,如今一切皆組件。web
ReactRouter中提供瞭如下三大組件:ajax
固然每一個組件下又會有幾種不一樣的子類組件實現。好比: Router組件就針對不一樣功能和平臺對應用:npm
<BrowserRouter>
瀏覽器的路由組件<HashRouter>
URL格式爲Hash路由組件<MemoryRouter>
內存路由組件<NativeRouter>
Native的路由組件<StaticRouter>
地址不改變的靜態路由組件三大組件使用的關係:bootstrap
若是說咱們的應用程序是一座小城的話,那麼Route就是一座座帶有門牌號的建築物,而Link就表明了到某個建築物的路線。有了路線和目的地,那麼就缺一位老司機了,沒錯Router就是這個老司機。
如今你能夠複製任意的示例代碼,並粘貼到 src/App.js
。以下:
import React, { Component } from 'react'; import { HashRouter as Router, Link, Route } from 'react-router-dom'; import './App.css'; const Home = () => ( <div> <h2>Home</h2> </div> ) const About = () => ( <div> <h2>About</h2> </div> ) const Product = () => ( <div> <h2>Product</h2> </div> ) class App extends Component { render() { return ( <Router> <div className="App"> <Link to="/">Home</Link> <Link to="/About">About</Link> <Link to="/Product">Product</Link> <hr/> <Route path="/" exact component={Home}></Route> <Route path="/about" component={About}></Route> <Route path="/product" component={Product}></Route> </div> </Router> ); } } export default App;
BrowserRouter
主要使用在瀏覽器中,也就是WEB應用中。它利用HTML5 的history API來同步URL和UI的變化。當咱們點擊了程序中的一個連接以後,BrowserRouter
就會找出與這個URL
匹配的Route
,並將他們對應的組件渲染出來。 BrowserRouter
是用來管理咱們的組件的,那麼它固然要被放在最頂級的位置,而咱們的應用程序的組件就做爲它的一個子組件而存在。
import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { BrowserRouter } from 'react-router-dom'; ReactDOM.render( <BrowserRouter> <App/> </BrowserRouter>, document.body);
BrowserRouter
組件提供了四個屬性。
basename
: 字符串類型,路由器的默認根路徑forceRefresh
: 布爾類型,在導航的過程當中整個頁面是否刷新getUserConfirmation
: 函數類型,當導航須要確認時執行的函數。默認是:window.confirm
keyLength
: 數字類型location.key
的長度。默認是 6當前位置的基準 URL。若是你的頁面部署在服務器的二級(子)目錄,你須要將 basename
設置到此子目錄。正確的 URL 格式是前面有一個前導斜槓,但不能有尾部斜槓。
例如:有時候咱們的應用只是整個系統中的一個模塊,應用中的URL老是以 http://localhost/admin/ 開頭。這種狀況下咱們總不能每次定義Link和Route的時候都帶上admin吧?react-router已經考慮到了這種狀況,因此爲咱們提供了一個basename屬性。爲BrowserRouter設置了basename以後,Link中就能夠省略掉admin了,而最後渲染出來的URL又會自動帶上admin。
<BrowserRouter basename="/admin"/> ... <Link to="/home"/> // 被渲染爲 <a href="/admin/home"> ... </BrowserRouter>
當導航須要確認時執行的函數。默認使用 window.confirm
。
// 使用默認的確認函數 const getConfirmation = (message, callback) => { const allowTransition = window.confirm(message) callback(allowTransition) } <BrowserRouter getUserConfirmation={getConfirmation}/>
當設置爲 true
時,在導航的過程當中整個頁面將會刷新。 只有當瀏覽器不支持 HTML5 的 history API 時,才設置爲 true
。
const supportsHistory = 'pushState' in window.history <BrowserRouter forceRefresh={!supportsHistory}/>
location.key
的長度。默認是 6。
<BrowserRouter keyLength={12}/>
渲染單一子組件(元素)。
HashRouter
使用 URL 的 hash (例如:window.location.hash
) 來保持 UI 和 URL 的同步。
注意: 使用 hash 的方式記錄導航歷史不支持
location.key
和location.state
。在之前的版本中,咱們爲這種行爲提供了 shim,可是仍有一些問題咱們沒法解。任何依賴此行爲的代碼或插件都將沒法正常使用。因爲該技術僅用於支持傳統的瀏覽器,所以在用於瀏覽器時可使用<BrowserHistory>
代替。
跟BrowserRouter
相似,它也有:basename
、getUserConfirmation
、children
屬性,並且是同樣的。
window.location.hash
使用的 hash 類型。有以下幾種:
"slash"
- 後面跟一個斜槓,例如 #/
和 #/sunshine/lollipops
"noslash"
- 後面沒有斜槓,例如 #
和 #sunshine/lollipops
"hashbang"
- Google 風格的 "ajax crawlable",例如 #!/
和 #!/sunshine/lollipops
默認爲 "slash"
。
主要用在ReactNative這種非瀏覽器的環境中,所以直接將URL的history保存在了內存中。 StaticRouter 主要用於服務端渲染。
Link就像是一個個的路牌,爲咱們指明組件的位置。Link使用聲明式的方式爲應用程序提供導航功能,定義的Link最終會被渲染成一個a標籤。Link使用to這個屬性來指明目標組件的路徑,能夠直接使用一個字符串,也能夠傳入一個對象。
import { Link } from 'react-router-dom' // 字符串參數 <Link to="/query">查詢</Link> // 對象參數 <Link to={{ pathname: '/query', search: '?key=name', hash: '#hash', state: { fromDashboard: true } }}>查詢</Link>
須要跳轉到的路徑(pathname)或地址(location)。
當設置爲 true
時,點擊連接後將使用新地址替換掉訪問歷史記錄裏面的原地址。
當設置爲 false
時,點擊連接後將在原有訪問歷史記錄的基礎上添加一個新的紀錄。
默認爲 false
。
<Link to="/courses" replace />
NavLink是一個特殊版本的Link,可使用activeClassName來設置Link被選中時被附加的class,使用activeStyle來配置被選中時應用的樣式。此外,還有一個exact屬性,此屬性要求location徹底匹配纔會附加class和style。這裏說的匹配是指地址欄中的URl和這個Link的to指定的location相匹配。
// 選中後被添加class selected <NavLink to={'/'} exact activeClassName='selected'>Home</NavLink> // 選中後被附加樣式 color:red <NavLink to={'/gallery'} activeStyle={{color:red}}>Gallery</NavLink>
activeClassName
默認值爲active
true
時,在肯定位置是否與當前 URL 匹配時,將考慮位置 pathname
後的斜線。Route應該是react-route中最重要的組件了,它的做用是當location與Route的path匹配時渲染Route中的Component。若是有多個Route匹配,那麼這些Route的Component都會被渲染。
與Link相似,Route也有一個exact屬性,做用也是要求location與Route的path絕對匹配。
// 當location形如 http://location/時,Home就會被渲染。 // 由於 "/" 會匹配全部的URL,因此這裏設置一個exact來強制絕對匹配。 <Route exact path="/" component={Home}/> <Route path="/about" component={About}/>
<Route path="/home" render={() => { console.log('額外的邏輯'); return (<div>Home</div>); }/>
1、它同render相似,是一個function。不一樣的地方在於它會被傳入一個match參數來告訴你這個Route的path和location匹配上沒有。
2、第二個特殊的地方在於,即便path沒有匹配上,咱們也能夠將它渲染出來。祕訣就在於前面一點提到的match參數。咱們能夠根據這個參數來決定在匹配的時候渲染什麼,不匹配的時候又渲染什麼。
// 在匹配時,容器的calss是light,<Home />會被渲染 // 在不匹配時,容器的calss是dark,<About />會被渲染 <Route path='/home' children={({ match }) => ( <div className={match ? 'light' : 'dark'}> {match ? <Home/>:<About>} </div> )}/>
全部路由中指定的組件將被傳入如下三個 props 。
這裏主要說下match.params.透過這個屬性,咱們能夠拿到從location中解析出來的參數。固然,若是想要接收參數,咱們的Route的path也要使用特殊的寫法。
以下示例,三個Link是一個文章列表中三個連接,分別指向三篇id不一樣的文章。而Route用於渲染文章詳情頁。注意path='/p/:id' ,location中的對應的段會被解析爲id=1 這樣的鍵值。最終這個鍵值會做爲param的鍵值存在。Route中的組件可使用this.props.match.params.id來獲取,示例中使用告終構賦值。
<Link to='/p/1' /> <Link to='/p/2' /> <Link to='/p/3' /> ...... <Route path='/p/:id' render={(match)=<h3>當前文章ID:{match.params.id}</h3>)} />
Location 是指你當前的位置,下一步打算去的位置,或是你以前所在的位置,形式大概就像這樣:
{
key: 'ac3df4', // 在使用 hashHistory 時,沒有 key
pathname: '/somewhere'
search: '?some=search-string',
hash: '#howdy',
state: {
[userDefined]: true
}
}
你使用如下幾種方式來獲取 location 對象:
this.props.location
的方式獲取,({ location }) => ()
的方式獲取,({ location }) => ()
的方式獲取,this.props.location
的方式獲取。你也能夠在 history.location
中獲取 location 對象,可是別那麼寫,由於 history 是可變的。更多信息請參見 history 文檔。
location 對象不會發生改變,所以你能夠在生命週期的鉤子函數中使用 location 對象來查看當前頁面的位置是否發生改變,這種技巧在獲取遠程數據以及使用動畫時很是有用。
componentWillReceiveProps(nextProps) {
if (nextProps.location !== this.props.location) {
// 已經跳轉了!
}
}
一般狀況下,你只須要給一個字符串當作 location ,可是,當你須要添加一些 location 的狀態時,你能夠對象的形式使用 location 。而且當你須要多個 UI ,而這些 UI 取決於歷史時,例如彈出框(modal),使用location 對象會有很大幫助。
// 一般你只須要這樣使用 location <Link to="/somewhere"/> // 可是你一樣能夠這麼用 const location = { pathname: '/somewhere' state: { fromDashboard: true } } <Link to={location}/> <Redirect to={location}/> history.push(location) history.replace(location)
最後,你能夠把 location 傳入一下組件:
這樣作可讓組件不使用路由狀態(router state)中的真實 location,由於咱們有時候須要組件去渲染一個其餘的 location 而不是自己所處的真實 location,好比使用動畫或是等待跳轉時。
本文檔中的「history」以及「history
對象」請參照 history
包中的內容。 History 是 React Router 的兩大重要依賴之一(除去 React 自己),在不一樣的 Javascript 環境中,history
以多種形式實現了對於 session 歷史的管理。
咱們會常用如下術語:
history
對象一般會具備如下屬性和方法:
length
-( number 類型)指的是 history 堆棧的數量。action
-( string 類型)指的是當前的動做(action),例如 PUSH
,REPLACE
以及 POP
。location
-( object類型)是指當前的位置(location),location 會具備以下屬性:
pathname
-( string 類型)URL路徑。search
-( string 類型)URL中的查詢字符串(query string)。hash
-( string 類型)URL的 hash 分段。state
-( string 類型)是指 location 中的狀態,例如在 push(path, state)
時,state會描述何時 location 被放置到堆棧中等信息。這個 state 只會出如今 browser history 和 memory history 的環境裏。push(path, [state])
-( function 類型)在 hisotry 堆棧頂加入一個新的條目。replace(path, [state])
-( function 類型)替換在 history 堆棧中的當前條目。go(n)
-( function 類型)將 history 對戰中的指針向前移動 n
。goBack()
-( function 類型)等同於 go(-1)
。goForward()
-( function 類型)等同於 go(1)
。block(prompt)
-( function 類型)阻止跳轉,(請參照 history 文檔)match
對象包含了 <Route path>
如何與URL匹配的信息。match
對象包含如下屬性:
params
-( object 類型)即路徑參數,經過解析URL中動態的部分得到的鍵值對。isExact
- 當爲 true
時,整個URL都須要匹配。path
-( string 類型)用來作匹配的路徑格式。在須要嵌套 <Route>
的時候用到。url
-( string 類型)URL匹配的部分,在須要嵌套 <Link>
的時候會用到。你能夠在如下地方獲取 match
對象:
this.props.match
方式。({ match }) => ()
方式。({ match }) => ()
方式當這個組件被渲染是,location會被重寫爲Redirect的to指定的新location。它的一個用途是登陸重定向,好比在用戶點了登陸並驗證經過以後,將頁面跳轉到我的主頁。
<Redirect to="/new"/>
渲染匹配地址(location)的第一個 <Route>
或者<Redirect>
這與只使用一堆<Route>
有什麼不一樣?
<Switch>
的獨特之處是獨它僅僅渲染一個路由。相反地,每個包含匹配地址(location)的<Route>
都會被渲染。思考下面的代碼:
<Route path="/about" component={About}/> <Route path="/:user" component={User}/> <Route component={NoMatch}/>
若是如今的URL是 /about
,那麼 <About>
, <User>
, 還有 <NoMatch>
都會被渲染,由於它們都與路徑(path)匹配。這種設計,容許咱們以多種方式將多個 <Route>
組合到咱們的應用程序中,例如側欄(sidebars),麪包屑(breadcrumbs),bootstrap tabs等等。 然而,偶爾咱們只想選擇一個<Route>
來渲染。若是咱們如今處於 /about
,咱們也不但願匹配 /:user
(或者顯示咱們的 "404" 頁面 )。如下是使用 Switch
的方法來實現:
import { Switch, Route } from 'react-router'
<Switch> <Route exact path="/" component={Home}/> <Route path="/about" component={About}/> <Route path="/:user" component={User}/> <Route component={NoMatch}/> </Switch>
如今,若是咱們處於 /about
, <Switch>
將開始尋找匹配的 <Route>
。 <Route path="/about"/>
將被匹配, <Switch>
將中止尋找匹配並渲染<About>
。 一樣,若是咱們處於 /michael
, <User>
將被渲染。
這對於過渡動畫也是起做用的,由於匹配的 <Route>
在與前一個相同的位置被渲染。
<Fade> <Switch> {/* there will only ever be one child here */} {/* 這裏只會有一個子節點 */} <Route/> <Route/> </Switch> </Fade> <Fade> <Route/> <Route/> {/* there will always be two children here, one might render null though, making transitions a bit more cumbersome to work out */} {/* 這裏老是有兩個子節點, 一個可能會渲染爲null, 使計算過渡增長了一點麻煩 */} </Fade>
當用戶離開當前頁的時候作出提示. 當你的應用處在特定狀態, 此狀態不但願用戶離開時(例如填寫表格到一半), 你應該使用<Prompt>
。
import { Prompt } from 'react-router' <Prompt when={formIsHalfFilledOut} message="你肯定要離開嗎?" />
當用戶嘗試導航離開時,提示用戶的消息。
<Prompt message="你肯定要離開嗎?"/>
會與用戶試圖前往下一個地址
(location) 和 action
一塊兒被調用。
函返回一個字符串用做向用戶提示,或者返回true
用做容許過渡。
<Prompt message={location => ( `你肯定你要前往 ${location.pathname} 嗎?` )}/>
你能夠隨時渲染<Prompt>
,而不是有條件地在警惕後面渲染它。
when={true}
時,通過用戶容許後導航when={false}
時,容許不用通過用戶容許直接導航<Prompt when={formIsHalfFilledOut} message="肯定嗎?"/>
import React from "react"; import { BrowserRouter as Router, Route, Link } from "react-router-dom"; // Some folks find value in a centralized route config. // A route config is just data. React is great at mapping // data into components, and <Route> is a component. //////////////////////////////////////////////////////////// // first our route components const Main = () => <h2>Main</h2>; const Sandwiches = () => <h2>Sandwiches</h2>; const Tacos = ({ routes }) => ( <div> <h2>Tacos</h2> <ul> <li> <Link to="/tacos/bus">Bus</Link> </li> <li> <Link to="/tacos/cart">Cart</Link> </li> </ul> {routes.map((route, i) => <RouteWithSubRoutes key={i} {...route} />)} </div> ); const Bus = () => <h3>Bus</h3>; const Cart = () => <h3>Cart</h3>; //////////////////////////////////////////////////////////// // then our route config const routes = [ { path: "/sandwiches", component: Sandwiches }, { path: "/tacos", component: Tacos, routes: [ { path: "/tacos/bus", component: Bus }, { path: "/tacos/cart", component: Cart } ] } ]; // wrap <Route> and use this everywhere instead, then when // sub routes are added to any route it'll work const RouteWithSubRoutes = route => ( <Route path={route.path} render={props => ( // pass the sub-routes down to keep nesting <route.component {...props} routes={route.routes} /> )} /> ); const RouteConfigExample = () => ( <Router> <div> <ul> <li> <Link to="/tacos">Tacos</Link> </li> <li> <Link to="/sandwiches">Sandwiches</Link> </li> </ul> {routes.map((route, i) => <RouteWithSubRoutes key={i} {...route} />)} </div> </Router> ); export default RouteConfigExample;