React-Router-Dom(路由版本[5])
简介
- React的一个插件库
- 用于实现SPA应用
- 基于React的项目基本都用
API
- <BrowserRouter>
- <HashRouter>
- <Route>
- <Redirect>
- <Link>
- <NavLink>
- <Switch>
其它
- 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可以用于解决一些路劲错误相关的问题