[TOC]
[API地址:react-router.docschina.org](https://react-router.docschina.org/)
# react-route
官網:https://reactrouter.com/web/guides/quick-start
React Router 4.0 (以下簡稱 RR4)它遵循 React 的設計理念,即萬物皆組件。
所以 RR4 只是一堆 提供了導航功能的組件(還有若干對象和方法),具有聲明式(**聲明式編程**簡單來講就是你只需要關心做什么,而無需關心如何去做,好比你寫 React 組件,只需要 render 出你想要的組件,至于組件是如何實現的是 React 要處理的事情。),可組合性的特點。
React Router V4 相對 V2/V3 幾乎完全重寫了,遵循 Just Component 的 API 設計理念。
| Package | Package | Docs | Description |
| --- | --- |--- |--- |
| [`react-router`](https://github.com/ReactTraining/react-router/blob/master/packages/react-router) | npm-v5.2.0 | [API Docs-site](https://reacttraining.com/react-router/core/guides/quick-start) | The core of React Router |
| [`react-router-dom`](https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom) | npm-v5.2.0 | [API Docs-site](https://reacttraining.com/react-router/web/guides/quick-start) | DOM bindings for React Router |
| [`react-router-native`](https://github.com/ReactTraining/react-router/blob/master/packages/react-router-native) | npm-v5.2.0 | [API Docs-site](https://reacttraining.com/react-router/native/guides/quick-start) | [React Native](https://facebook.github.io/react-native/) bindings for React Router |
| [`react-router-config`](https://github.com/ReactTraining/react-router/blob/master/packages/react-router-config) | npm-v5.1.1 | [API Docs-readme](https://github.com/ReactTraining/react-router/blob/master/packages/react-router-config/#readme) | Static route config helpers |
## react-router 還是 react-router-dom?
在 React 的使用中,我們一般要引入兩個包,`react` 和 `react-dom`,那么 `react-router` 和 `react-router-dom` 是不是兩個都要引用呢?
非也,坑就在這里。
1. React-router
React-router 提供了一些 router 的核心 api,包括 `Router`, `Route`, `Switch`等,但是它沒有提供 dom 操作進行跳轉的 api , **服務器端渲染**很適合。
2. react-router-dom
`React-router-dom` 提供了`BrowserRouter`,`HashRouter`,`Link`等等在 web 端常用的 DOM 類組件,我們可以通過 dom 的事件控制路由。例如點擊一個按鈕進行跳轉,大多數情況下我們是這種情況,所以在開發過程中,我們更多是使用 React-router-dom。
兩個只要引用一個就行了,因此我們只需引用 `react-router-dom` 這個包就行了。當然,如果搭配 redux ,你還需要使用 `react-router-redux`。
[what is the diff between react-router-dom & react-router?](https://github.com/ReactTraining/react-router/issues/4648)
# react-router v5
v5 中最重要的改進是對 >= 15 版本的?React 完全兼容,并對 React 16 提供了更好的支持。
升級了 React 的 context API;消除了所有?<StrictMode> 警告;對捆綁基礎架構進行了徹底檢查,并對發布的所有綁定包進行了全面測試。
還為生產引入了預優化的 build,可以不用在構建腳本中手動將 `process.env.NODE_ENV` 設置為`production`,更重要的是不會將路由器構建為 build 的一部分,團隊已經在開發和生產模式中處理了這個問題。
導入方式也因此需要改變:
```
// Instead of:
import Router from 'react-router/Router';
import Switch from 'react-router/Switch';
// do:
import { Router, Switch } from 'react-router';
```
目前仍然支持前一種風格,但會發出警告。
此外,v5 簡化并自動化了發布過程,從現在開始能夠更頻繁、更可預測地發布。
新特性方面,此版本的一個主要新功能是能夠在 <Route path> 中使用數組,簡化了操作:
```
// Instead of this:
<Switch>
<Route path="/users/:id" component={User} />
<Route path="/profile/:id" component={User} />
</Switch>
// you can now do this:
<Route path={["/users/:id", "/profile/:id"]} component={User} />
```
還帶來了一些 bug 修復,包括支持 <Link innerRef> 中的 `React.createRef`,并支持在 `<Route component>` 中使用 `React.forwardRef`。
> 詳情查看發布公告
> https://reacttraining.com/blog/react-router-v5/
# 詳解
現在改版之后,我們引入的包是 `react-router-dom` 包。
改版之后的 `react-router-dom` 路由,我們要理解三個概念,`Router`、`Route`和`Link`。
## `<Router>`
`<Router>` 是所有路由組件共用的底層接口,一般我們的應用并不會使用這個接口,而是使用高級的路由:
* `<BrowserRouter>`:使用 HTML5 提供的 history API 來保持 UI 和 URL 的同步;url 是這樣的:`/user/liuna`
* `<HashRouter>`:使用 URL 的 hash (例如:`window.location.hash`) 來保持 UI 和 URL 的同步; url 是這樣的:`/#/user/liuna?\_k=adseis`
* `<MemoryRouter>`:能在內存保存你 “URL” 的歷史紀錄 (并沒有對地址欄讀寫);可以被用在任何可以運行 js 的環境中;通常被用來做單元測試(沒有瀏覽器環境);自己維護一個 location array的。
* `<StaticRouter>`:從不會改變地址;
使用方式:
```
import { BrowserRouter as Router } from "react-router-dom";
class Main extends Component{
render(){
return(
<Router>
<div> // Router 組件有且只有一個根節點
//otherCoding
</div>
</Router>
)
}
}
```
## `<Link>`
Link是 react 路由中的點擊切換到哪一個組件的鏈接,(這里之所以不說是頁面,而是說組件,因為切換到另一個界面只是展示效果,react 的本質還是一個單頁面應用-single page application)。
基本使用方式:
```js
import { BrowserRouter as Router, Link} from "react-router-dom";
class Main extends Component{
render(){
return(
<Router>
<div>
<ul>
<li><link to='/'>首頁</Link></li>
<li><link to='/other'>其他頁</Link></li>
</ul>
</div>
</Router>
)
}
}
```
特別說明:
第一、`<Router>` 下面只能包含一個盒子標簽,類似這里的div。?
第二、`<Link>` 代表一個鏈接,在 html 界面中會解析成 a 標簽。作為一個鏈接,必須有一個 to 屬性,代表鏈接地址。這個鏈接地址是一個相對路徑。?
第三、`<Route>`,是下面要說的組件,有一個 path 屬性和一個組件屬性(可以是 component、render 等等)。
## `<Route>`?
* `Route` 代表了你的路由界面,`path` 代表路徑,`component` 代表路徑所對應的界面。
* 在其 `path` 屬性與某個 `location` 匹配時呈現一些 UI (`component`)。
使用方式:
```
import React,{ Component } from "react";
import { render } from "react-dom";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
// home.js
class Home extends Component{
render(){
return (
<div>this a Home page</div>
)
}
}
// other.js
class Other extends Component{
render(){
return (
<div>this a Other page</div>
)
}
}
// app.js
class Main extends Component{
render(){
return (
<Router>
<div>
<ul> // 除了路由組件,可以寫入其他標簽,默認 Route 所在的位置為路由組件顯示的容器
<li><Link to="/home">首頁</Link></li> //(tips:Link寫在Router內部形成路由結構)
<li><Link to="/other">其他頁</Link></li>
</ul>
<Route path="/home" component={Home}/>
<Route path="/other" component={Other}/>
</div>
</Router>
)
}
}
render(<Main />,document.getElementById("root"));
```
## `<Switch>`
用于渲染與路徑匹配的第一個子`<Route>`或`<Redirect>`。
~~~
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>` 會呈現。
## `<Redirect>`
通過 `Redirect` 組件對象,設置 `to` 屬性
```
<Redirect to {{
pathname: '/about',
search: '?utm=something',
state: { referrer: someplage.com }
}}>
```
## 路由參數傳遞
```
?? ?/a/1 ---this.props.match.params.id
?? ?/a?id=1---this.props.location.query.id
```
## 路由切換跳轉
```
?? ?this.props.history.push({ pathname: '/detail', state: { id: 3 } })
....
this.props.history.location.state // 獲取 傳過來的參數
```
?? ?因為 BrowserRouter ?相當于 `<Router history={history}>` 故可直接通過 history 進行 `push` 跳轉
## withRouter
[`withRouter`](https://reactrouter.com/web/api/withRouter) 是`react-router-dom`中的一個高階組件 ,要先引入才能使用:
```
import { BrowserRouter as Router, Route, Link, Switch, withRouter, RouteComponentProps } from 'react-router-dom'
```
使用高階組件時有兩種方法:
1??函數式調用:
```
// @ts-ignore
export default withRouter(SystemSider)
```
2??裝飾器:
```
// @ts-ignore
@withRouter
```
正常情況下 只有 Router 組件能夠自動帶有三個屬性 如下的 Home 組件有
```
var Home = ( {history,location,match})=> <div>{location.pathname}</div>
<Route exact path="/Home" component={Home}/>
```
`withRouter`作用是將一個組件包裹進 Route 里面,然后 react-router 的三個對象`history`, `location`, `match`就會被放進這個組件的`props` 性中。
```
import React from 'react';
import { connect } from 'dva';
import styles from './nav.css';
import { Route, Switch, routerRedux,withRouter } from 'dva/router'; // dva寫法
// import { withRouter } from 'react-router-dom'; // 普通react項目寫法
class Nav extends React.Component {
jumping() {//跳轉到
this.props.history.push('/')
console.log(this.props)
}
render() {
const { match, location, history } = this.props;
return (
<div className={styles.box}>
<div className={styles.navBox}>
<div className={styles.iconBox}>
<div className={styles.title}>公路地質災害時空數據監測預警系統</div>
</div>
<div>You are now at {location.pathname}</div>
<div className={styles.listItem} onClick={this.jumping.bind(this)}>動態多維可視化子系統</div>
</div>
</div>
</div>
);
}
}
export default withRouter(Nav);
```
`這個`例子中點擊`div`利用`history`跳轉到別的頁面。使用`withRouter`后,`this.props`里才會有`history`屬性。
`withRouter` 的作用就是,但是我們要點擊某個元素去跳轉一個頁面,比如點擊頁面的 logo 返回首頁,這時候就可以使用 `withRouter` 來做.
## 其他函數
### `replace`
有些場景下,重復使用 `push` 或 `a` 標簽跳轉會產生死循環,為了避免這種情況出現,react-router-dom 提供了`replace`。在可能會出現死循環的地方使用 `replace` 來跳轉:
```
this.props.history.replace('/detail');
```
### `goBack`
場景中需要返回上級頁面的時候使用:
```
this.props.history.goBack();
```
# react-router-redux
https://segmentfault.com/q/1010000010489394
react-router-redux 是將 react-router 和 redux 集成到一起的庫,讓你可以用 redux 的方式去操作 react-router。
例如,react-router 中跳轉需要調用 `router.push(path)`,集成了 react-router-redux 你就可以通過 `dispatch` 的方式使用 router,例如跳轉可以這樣做 `store.dispatch(push(url))`。
本質上,是把 react-router 自己維護的狀態,例如 location、history、path 等等,也交給 redux 管理。一般情況下,是沒有必要使用這個庫的。
# 示例
[How To Create A Multi-Page Website With React In 5 Minutes](
https://www.techomoro.com/how-to-create-a-multi-page-website-with-react-in-5-minutes/)
[techomoro/ReactMultiPageWebsite](https://github.com/techomoro/ReactMultiPageWebsite)
https://www.kirupa.com/react/creating_single_page_app_react_using_react_router.htm
# 參考
[React Router:從V2/V3遷移到V4 ](https://github.com/YutHelloWorld/Blog/issues/4)
[react-router V4 中三種 router 區別?](https://www.zhihu.com/question/63662664)
[H5 history API 解析](https://www.jianshu.com/p/daf1c1b93c5c/)
[性能 & 集成 —— History API](https://juejin.im/post/5c5313905188257a4a7fbeab#heading-9)
[Router5](https://router5.js.org/)