重学 React 之基础认知
重学 React 之三大属性
重学 React 之生命周期
在我当初第一次学 react 时,我一直没弄明白 react 路由的使用,这次我终于自己理清楚了。本篇将介绍常用路由组件的作用、路由组件传参以及编程式导航。
长话短说,开发 react web 应用,使用的路由库是 react-router
中的 web 版本,即 react-router-dom
。注意如果是用 create-react-app
脚手架创建的项目,需要单独安装 react-router-dom
。
react-router-dom
中暴露了一些实现路由切换的组件,也是我们要学习的部分。
本文 React 用例版本:v17.0.2
Link,Route,BrowserRouter
使用这三个组件就可以完成路由切换,先看例子再介绍:
import {Link, Route, BrowserRouter} from 'react-router-dom'
<BrowserRouter>
<Link to="/home">home</Link>
<Link to="/about">about</Link>
<Route path='/home'> <Home/> </Route>
<Route path='/about'> <About/> </Route>
</BrowserRouter>
Link
组件,相当于 a
标签,其实最终在页面上也是渲染成 a
标签,它的作用就是切换路由的,当点击时,会激活对应的路由路径,它的 to
属性就是标记路由路径的。用过 vue 的一定熟悉 <router-link>
,跟 Link
类似。
Route
组件,用于注册路由,path
属性匹配当前路由路径,,component 属性传入当前路径匹配的组件,当 path
匹配到时,就渲染 component
传入的组件。
也可以将要渲染的组件,直接写在组件标签中,如下:
<Route path='/home'> <Home/> </Route>
<Route path='/about'> <About/> </Route>
添加 exact
开启严格匹配模式,即 path 值需要跟 Link
中的 to 的值一致时才匹配。
BrowserRouter
组件,不知道怎么解释比较好,可以理解为要在路由应用的最外层,使用 BrowserRouter
包裹。
其实更准确一点应该是将互相映射的 Link
和 Route
(不知道这么表达对不对)包裹在同一个 BrowserRouter
下,也就是说如果像下面这样分开包裹就是错的:
<BrowserRouter>
<Link to="/home">home</Link>
<Link to="/about">about</Link>
</BrowserRouter>
<BrowserRouter>
<Route path='/home' component={Home}></Route>
<Route path='/about' component={About}></Route>
</BrowserRouter>
分开使用 BrowserRouter
包裹,是无法匹配到对应组件的。有时为了方便,可以将 BrowserRouter
组件应用到入口文件中,如下:
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
这样在你的组件中就不需要再引入 BrowserRouter
了,可以随意位置写 Link
和 Route
了。
实际开发中,也不会像开头的例子一样,将它们全写在一起,这样不灵活,只需记住,互相匹配的 Link 和 Route 在同一个 BrowserRouter
即可。
HashRouter
作用与 BrowserRouter
一样,只是从 history 式路由,变成 hash 式路由。
跟 vue-router 中一样的,一般 SPA 应用都有这两种路由模式,原理这里就不再展开说明,社区有很多相关文章。
NavLink
作用与 Link
一样,只是在路由激活时,会在渲染后的 a 标签上加上 active
类名 ,这样的好处就是可以定义路由激活时的样式,只需要对 active 类定义样式。
同时可以修改激活时的类名,使用 activeClassName
属性自定义激活类名:
<NavLink activeClassName="selected" to="/about">about</NavLink>
<NavLink activeClassName="selected" to="/home">home</NavLink>
最终渲染的 dom 元素上会带上这个 class 类名
Switch
Switch
组件用来包裹 Route
的,它可以提高路由匹配效率,Route 匹配路由时,即使匹配到了还会继续往后面匹配,例如:
<Route path='/home' component={Home}></Route>
<Route path='/home' component={Test}></Route>
上面代码中两个 Route
匹配的 path
都是 /home
,最终 Home
和 Test
组件都会渲染到页面。
如果有很多个 Route
,也会一直往后面匹配直至结束,这显然影响效率。
如果要避免这种情况,就可以使用 Switch
包裹,这样就是单一路由匹配,匹配到就结束。
<Switch>
<Route path='/home' component={Home}></Route>
<Route path='/home' component={Test}></Route>
</Switch>
此时,Test
组件就不会再匹配。
Redirect
重定向组件,可以在匹配规则后重定向到指定路由。
当用在 Switch
中时,可以将 Redirect
放到最后面用于“兜底”作用,即上面所有的路由都没匹配到时,重定向,例:
<Switch>
<Route path='/home' component={Home}></Route>
<Route path='/about' component={About}></Route>
// 当上面两个都没匹配到时,重定向到 /home
<Redirect to="/home" />
</Switch>
当直接用在组件中时,组件渲染时就会触发重定向:
import React, { Component } from 'react'
import { Redirect } from 'react-router-dom'
export default class Test extends Component {
render() {
return (
<div>
Test 组件,当我渲染时会重定向到 /about
<Redirect to='/about' />
</div>
)
}
}
向路由组件传递参数
向路由组件传递参数有三种常见方法:
1. 传递 params 参数
参数值直接拼接在路由链接上,例如要向文章页面传递文章 id 参数:
// 123 是要传递的文章id
<Link to='/article/123'></Link>
路由组件需要声明接收id
参数:
<Route path='/article/:id' component={Article}/>
然后在组件内部获取参数:
// 获取传递过来的 params 参数
this.props.match.params // {id: '123'}
2. 传递 search 参数
参数值也是拼接在路由链接上,但是需要用query的方式,即加 ?
前缀,还是上面的例子,search 方式:
<Link to='/article?id=123'></Link>
路由组件不需要声明接收 id
参数:
<Route path='/article' component={Article} />
组件内部获取参数:
// 获取 search 参数
this.props.location.search // ?id=123
获取到的是参数字符串,需要自行格式化,可以 借助 query-string
库进行参数格式化:
import querystring from 'query-string'
querystring.parse(this.props.location.search) // {id:'123'}
3. 传递 state 参数
参数值需要已对象的方式传递:
<Link to={{pathname:'/article',state:{id:123}}}></Link>
路由组件也不需要声明接收 id
参数:
<Route path='/article' component={Article} />
组件内获取参数:
// 获取 state 参数
this.props.location.state // {id: '123'}
注:此方式刷新页面时,如果启用的是 BrowserRouter
,状态不会消失,如果用的是 HashRouter
,状态会消失。
编程式路由导航
React 中使用编程式导航,主要利用路由组件中的 props
属性的 history
对象中的 push
,replace
, go
,goBack
和 goForward
等 api。
这样使用:this.props.history.push('/home')
,使用很简单,这里就不展开说明了。
withRouter
只有路由组件的 props
上才有路由参数,才能进行编程式导航,非路由组件是不能直接进行编程式导航的。
所谓非路由组件,即不是通过 Route
切换的组件,比如 react 应用中的 App.js
这个组件,一般是首页入口,是通过浏览器输入地址打开的,而不是通过路由跳转的,它就不能直接使用编程式导航,它的 this.props
为空。
而 withRouter
的作用就是将路由参数 history
、location
和 match
三个对象传入组件的 props
对象上,它的使用方法也很简单,直接包裹组件即可,例子:
import React, { Component } from "react";
import { Route, withRouter } from "react-router-dom";
import Message from "./pages/Message";
import Home from "./pages/Home";
class App extends Component {
render() {
console.log(this.props); // 只有被 withRouter 包裹时,props 上才有路由参数
return (
<div className="App">
<Route path="/message" component={Message}></Route>
<Route path="/home" component={Home}></Route>
</div>
);
}
}
export default withRouter(App); // 直接在这里包裹即可
结尾
如有错误,欢迎指出。