[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/)