React-Router-Dom(路由版本[5])
简介
- React的一个插件库
- 用于实现SPA应用
- 基于React的项目基本都用
API
其它
- history对象
- match对象
- withRouter函数
添加依赖
yarn add react-router-dom@5
使用
BrowserRouter+Link+Route
- import {Link, BrowserRouter, Route} from 'react-router-dom'
- // 需要在最外面包裹一个路由管理器 也可以包在index.js的app组件外面
- <BrowserRouter>
- <div style={{textAlign: 'center'}}>
- <div>
- {/*定义导航连接*/}
- <Link to="/home">Home</Link>
- <br/>
- <Link to="/about">About</Link>
- </div>
- <div>
- {/*注册路由*/}
- <Route path="/about" component={About}/>
- <Route path="/home" component={Home}/>
- </div>
- </div>
- </BrowserRouter>
BrowserRouter+NavLink+Route
和Link功能一样, 但是会在点击的时候自动追加和移除一个class,那就是active, 可以通过属性activeClassName修改
- .active {
- background-color: skyblue;
- }


就是一个这样的效果
BrowserRouter+NavLink+Switch+Route
主要说一下Switch的用法的作用
- import {Link, BrowserRouter, Route, NavLink, Switch} from 'react-router-dom'
- <Switch>
- <Route path="/about" component={About}/>
- <Route path="/home" component={Home}/>
- <Route path="/home" component={About}/>
- </Switch>
在注册路由时可以使用Switch包裹, 如果不使用, 那么路由匹配遇到相同的, 还会继续往下匹配,并且全部展示
不包裹VS包裹


包裹后, 遇到第一个匹配的路由,就会展示并返回, 不往下继续匹配
样式丢失问题解决[扩展]
index.html
- 在引用样式的时候写%PUBLIC_URL%
- 使用绝对路径
- 使用HashRouter[基本不用]
模糊匹配与精准匹配
默认采用模糊匹配
路由中包含传递的值,即可展示


还是可以展示的,但是路径已经变成了/home/a/b
使用exact={true}可以开启精准匹配


开启精准匹配后再次访问, 就没有展示了
BrowserRouter+NavLink+Switch+Route+Redirect
- import {Link, BrowserRouter, Route, NavLink, Switch, Redirect} from 'react-router-dom'
- import React, {Component} from 'react';
- import {Link, BrowserRouter, Route, NavLink, Switch, Redirect} from 'react-router-dom'
- import About from "./components/About";
- import Home from "./components/Home";
- import './App.css'
-
- class App extends Component {
- render() {
- return (
- // 需要在最外面包裹一个路由管理器 也可以包在index.js的app组件外面
- <BrowserRouter>
- <div style={{textAlign: 'center'}}>
- <div>
- {/*定义导航连接*/}
- <NavLink to="/home">Home</NavLink>
- <br/>
- <NavLink to="/about">About</NavLink>
- </div>
- <div>
- {/*注册路由*/}
- <Switch>
- <Route path="/about" component={About}/>
- <Route path="/home" component={Home}/>
- <Redirect to="/home" />
- </Switch>
- </div>
- </div>
- </BrowserRouter>
- );
- }
- }
- export default App;
Redirect, 就是重定向的意思, 用于路由中没有匹配到路径的情况, 就会走Redirect重定向到指定路径
输入

默认会中定向到home

嵌套路由使用
- import React, {Component} from 'react';
- import {NavLink, Redirect, Route, Switch} from "react-router-dom";
- import News from "../../pages/News";
- import Messages from "../../pages/Messages";
-
- class Index extends Component {
- render() {
- return (
- <div>
- <h2>this is home...</h2>
- <div>
- <h4>导航</h4>
- <NavLink to="/home/news">News</NavLink>
- <br/>
- <NavLink to="/home/messages">Messages</NavLink>
- </div>
- <div style={{textAlign: 'center'}}>
- <h4>内容</h4>
- <Route path="/home/news" component={News}/>
- <Route path="/home/messages" component={Messages}/>
- </div>
- </div>
- );
- }
- }
-
- export default Index;
在Home组件中继续使用NavLink+Route注册路由, 但是需要携带前缀, 并且在外部不能开启精准模式,不然会造成匹配不到的情况,二级路由也可以使用Redirect实现默认选中

路由组件传递参数[params]
- import React, {Component} from 'react';
- import {Link, NavLink, Route} from "react-router-dom";
- import Detail from "./Detail";
-
- class Index extends Component {
- state = {
- items: [
- {id: '1', name: 'React'},
- {id: '2', name: 'Vue'},
- {id: '3', name: 'Ts'}
- ]
- }
-
- render() {
- const {items} = this.state
- return (
- <div>
- <ul>
- {
- items.map(item => {
- return (
- <li key={item.id}>
- {/* 向路由组件传递params参数 */}
- <Link to={`/home/messages/detail/${item.id}/${item.name}`}>{item.name}</Link>
- </li>
- )
- })
- }
- </ul>
- <hr/>
- {/*接收params参数*/}
- <Route path='/home/messages/detail/:id/:title' component={Detail} />
- </div>
- );
- }
- }
-
- export default Index;
- import React, {Component} from 'react';
-
- const data = [
- {id: '1', content: 'this is React'},
- {id: '2', content: 'this is Vue'},
- {id: '3', content: 'this is Ts'}
- ]
-
- class Index extends Component {
- render() {
- const {id, title} = this.props.match.params
- const item = data.filter(x=>x.id===id)[0]
- return (
- <div>
- <div>id:{item.id}</div>
- <div>name:{title}</div>
- <div>content:{item.content}</div>
- </div>
- );
- }
- }
-
- export default Index;

通过路径参数传递
路由组件传递参数[search]
- {/* 向路由组件传递search参数 */}
- <Link to={`/home/messages/detail?id=${item.id}&title=${item.name}`}>{item.name}</Link>
- {/*接收search参数 不需要*/}
- <Route path='/home/messages/detail' component={Detail}/>
- import React, {Component} from 'react';
-
- const data = [
- {id: '1', content: 'this is React'},
- {id: '2', content: 'this is Vue'},
- {id: '3', content: 'this is Ts'}
- ]
-
- class Index extends Component {
- render() {
- // 接收Params参数
- // const {id, title} = this.props.match.params
- const {id,title} = this.getSearch()
- const item = data.filter(x=>x.id===id)[0]
- return (
- <div>
- <div>id:{item.id}</div>
- <div>name:{title}</div>
- <div>content:{item.content}</div>
- </div>
- );
- }
- // 获取search参数
- getSearch = () => {
- let search = this.props.location.search
- search = search.substring(1);
- let conditions = search.split("&")
- let obj = {}
- conditions.map(condition => {
- const kv = condition.split("=")
- obj[kv[0]] = kv[1]
- })
- return obj
- }
- }
-
- export default Index;
在这里 我是自己实现了参数的解析, 也可以使用querystring的方法, 当热这个库在React18之后已经被弃用了, 本来我也想试一下的,但是发现不行

调用直接报错

应该是已经没有依赖了, 可以自己安装一下, 我就不安装了
路由组件传递参数[state(和组件的state没有关系)]
- {/* 向路由组件传递state参数[和组件的state没有关系] */}
- <Link to={{pathname:'/home/messages/detail',state:{id:item.id,title:item.name}}}>{item.name}</Link>
- {/*接收state参数 不需要*/}
- <Route path='/home/messages/detail' component={Detail}/>
- import React, {Component} from 'react';
-
- const data = [
- {id: '1', content: 'this is React'},
- {id: '2', content: 'this is Vue'},
- {id: '3', content: 'this is Ts'}
- ]
-
- class Index extends Component {
- render() {
- // 接收Params参数
- // const {id, title} = this.props.match.params
- // 接受Search参数
- // const {id,title} = this.getSearch()
- // 接受state参数
- const {id, title} = this.props.location.state
- const item = data.filter(x=>x.id===id)[0]
- return (
- <div>
- <div>id:{item.id}</div>
- <div>name:{title}</div>
- <div>content:{item.content}</div>
- </div>
- );
- }
- // 获取search参数
- getSearch = () => {
- let search = this.props.location.search
- search = search.substring(1);
- let conditions = search.split("&")
- let obj = {}
- conditions.map(condition => {
- const kv = condition.split("=")
- obj[kv[0]] = kv[1]
- })
- return obj
- }
- }
-
- export default Index;
可以直接从location.state上获取, 并且不会在地址栏上显示
replace与push
默认使用push,采用压栈方式存储历史记录, 可以通过back,go来完成前进或者后退
可以修改为replace替换, 默认会替换栈顶部的历史记录, 所以不会留下痕迹, 也就不能通过back,go完成前进和后退

修改方式为, 增加replace属性
编程试路由导航
- <Link replace to={{
- pathname: '/home/messages/detail',
- state: {id: item.id, title: item.name}
- }}>{item.name}</Link>
- <button style={{marginLeft: '5px'}} onClick={event => {
- this.show(item.id, item.name, 'push')
- }}>push</button>
- <button style={{marginLeft: '5px'}} onClick={event => {
- this.show(item.id, item.name, 'replace')
- }}>replace</button>
- <Route path='/home/messages/detail/:id/:title' component={Detail}/>
- show = (id, title, type) => {
- // replace | push 路由跳转
- this.props.history[type](`/home/messages/detail/${id}/${title}`)
- }
通过props对象上的history对象调用方法实现编程式路由跳转
这个案例是用params参数的方式,如果是search方式就自己改一下问号, 如果是state方式, 就把对象放入参数的第二个参数,第一个是URL, 第二个就是state
路由组件与一般组件
- # 直接使用定义的组件 就是一般组件 渲染时props中不会有默认路由组件的三大对象
- <Header />
- # 通过路由跳转的组件 就是路由组件 渲染时props中会携带 history location match 三大对象
- <Route path="/home" component={Home}/>
如果想要在一般组件中使用路由组件的三大对象, 那么就需要withRouter函数
withRouter
- import React, {Component} from 'react';
- import {withRouter} from "react-router-dom";
-
- class Index extends Component {
- render() {
- console.log(this)
- return (
- <div>
- <h2>this is Header</h2>
- </div>
- );
- }
- }
-
- export default withRouter(Index);

这样就可以获取到三大对象了
路由默认入参对象总结
| 对象 | 函数/属性 | 作用 |
| history | go(n) | 传入一个number数值,1代表前进一步,-1代表后退一步, 2代表前进两步 |
| goBack() | 后退一步 | |
| goForward() | 前进一步 | |
| push(uri, state) | push方式跳转路由, 第一个参数是路由地址, 第二个是state对象 | |
| replace(uri,state) | replace方式跳转路由, 第一个参数是路由地址, 第二个是state对象 | |
| location | pathname | 路由地址 |
| search | search方式传参的获取位置 | |
| state | state方式传参的获取位置 | |
| match | params | params方式传参的获取位置 |
| path | 路由地址 | |
| url | 路由地址 |
BrowserRouter和HashRouter的区别
- 底层原理不一样
- BrowserRouter使用的是H5的History API不兼容IE9及其以下的版本
- HashRouter使用的是URL的哈希值
- URL的表现形式不一样
- BrowserRouter的路径中没有#, 例如http://localhost:3000/home
- HashRouter的路径包含#, 例如http://localhost:3000/#/home
- 刷新后对路由state参数的影响
- BrowserRouter没有任何影响, 应为state保存在History对象中
- HashRouter刷新会导致路由state参数的丢失
- 扩展: HashRouter可以用于解决一些路劲错误相关的问题
