React Router 5
前端路由模式
Hash
兼容性好。 但是地址栏带#影响美观,依靠锚点和onhashchange事件实现页面的切换
History
地址栏较为美观,没有突兀的#号
缺点:
- 上线部署时需要结合后端的配置
- 兼容性略差 依赖js点击事件和onpopstate事件实现页面的切换
与Vue-router相比较
vue:配置式路由 React:组件式路由:默认无配置式路由,也可以自行配置式路由
React路由的使用
安装路由模块
这里学习的是5版本,当前最新为6版本
npm -S react-router-dom@5
引入路由模式
在项目的入口文件中定义当前项目使用的路由模式。
BrowserRouter:history模式
HashRouter:哈希模式
根据需求在依赖中引入
import { BroswerRouter } from 'react-router-dom'
在组件上嵌套应用
(这里将App作为项目的根组件)
<BroswerRouter>
<App />
</BroswerRouter>
但是这样的引用方式,如果未来切换路由模式,需要改动的地方过多,可以采用别名的方式引入
import { BrowserRouter as Router } from 'react-router-dom'
这样,在使用的时候可以这样引用
<Router> <App/> </Router>
定义路由规则
准备工作 引入:
import { Route } from 'react-router-dom'
然后在组件中引入想要渲染的组件
.....
类似于vue-router中的router-view标签,react使用的是Router标签
<Router path='/home' component ={Home}>
path:url路径
component:路径所匹配展示的组件名
都是书写在jsx的语法中
注意: 默认情况下,react-route-dom的path是模糊匹配,且会一直向下匹配,直到没有规则就停止。
解决: 1.采用严格匹配 2.引入Switch形成路由表方式的匹配
Switch
1.引入:
import { Switch } from 'react-router-dom'
2.使用:
<Switch>
<Router path:'/about'/>
<Router path:'/' />
<Router path:'*' />// * 代表上面没有匹配路由,404页面
</Switch>
把范围大的写在下面,范围小的写在上面, /about在/上面
什么都匹配不到就会匹配'*'中的内容,常用于404
声明式导航
声明式导航最终都会解析成a标签
引入:
import { Link,NavLink } from 'react-router-dom'
Link标签
<Link to='/home' exact>内容</Link>
- to:string | object :路由跳转的地址
- exact:严格匹配(不常用)
NavLink标签
<NavLink
to='/home'
avtiveClassName='xxx'
exact = { true }//严格模式匹配。实例
>
内容
</NavLink>
特点:
- 激活样式:点击时添加了active类名
- 可以自定义激活样式名avtiveClassName='xxx'
to属性的写法(对象形式):
to={{ pathname:'/home',query:{ id:xxx } }}
to={{ pathname:'/home',search:'id:xxx ' }}
编程式导航
特点:
- 通过js来完成导航切换,通过this.props中的history对象push/replace/goBack等方法实现编程式导航功能。
- 要求:只有当前组件被路由规则匹配到,才会有this.props.history对象
- 被匹配组件的子组件没有this.props.history对象,在此子组件中无法直接进行编程式导航
子组件中没有this.props,history的解决:
解决:父组件将history传递
<Son history = {this.props.history}>
<Son { ...this,props }/> 将父组件中的全部属性传给子组件
跨页面传参
动态路由(param)
作用:一般用于详情页,必须先定义规则,一般用于详情页
<Route path='/detail/:xx' component={Xx} />
带参跳转:
<Link to='/detail/2'>
可选动态路由:
<Route path='/detail/:xx?' component={Xx} />
动态路由后面加'?',代表没有动态参数传入的时候也可以跳转.
获取动态路由参数:
在落地组件中通过this.props.match.params得到
在路由成功匹配跳转后的动态路由组件中,可以通过{...props}传入子组件
查询字符串(search)
作用:通过地址栏中的 ?key=value&key=value传递
获取:在落地组件中通过this.props.location.search得到
//获取对象形式的查询字符串
const search = new URLSearchParams(..localtion.search)
search.get('key名')
const search = new URLSearchParams(..localtion.search)
let res={}
for(let [key,value] of search.entries()) {
res[key]=value
}
隐式传参(state)
注意:要想state传递,声明式导航必须写成对象方式
特点:地址栏无法查看传递的参数
定义:
编程式导航:
this.props.history.push({
pathname:'',
state:{key:value}
})
声明式导航:
<Link to = {{
pathname:'detail:/3'
state:{age:100}
}}>
参数的获取:
this.props.location.state //此时会得到一个对象
隐式传参问题:
在hash模式中此方案会有刷新丢失数据的情况
路由嵌套(子路由)
路由中前缀相同,可以嵌套,前缀后面的不同地址显示不同的组件。
home/login
home/register
路由表配置:
//入口文件中:
<Router path='/admin' component={Admin}>
</Router>//切记父路由不要设置严格模式
父组件中
//在要展示子路由的位置
<Switch>
<Route path='/admin/xx1',component={xx1}>
<Route path='/admin/xx1',component={xx2}
</Switch>
路由的三种渲染方式(注意在6版本中不存在)
component(组件对象或函数)
类渲染方式:
<Router path='/ component={ XX }' ></Router>
特点:
1. 自动把路由对象映射到渲染组件的props对象中
2.类在路由规则匹配的环节中不能写业务判断,如当前只有登录才能访问,不能写判断
函数渲染方式:
<Router path='/home' component={router => {
return <Home {...router} />
}} />
特点:
1. 使用函数回调的方式,要返回jsx。
2. 需要手动的把router传入到渲染组件的props中
3. 可以在匹配规则后,进行业务判断,如果满足则渲染
重点: 函数渲染方式父组件内重新渲染, 子组件也会销毁重新渲染. 类组件不会重复创建渲染
render(函数)
作用:结合了component类和函数组件中的优点不会重新渲染,自定义判断条件
<Router path='' render={router => {
if(router.location.search=='?xx=xx'){
return <Home { ...router }/>//需要手动传递props
}else if(router.location.search=='?xx=xx'){
return <>参数差太多<>
}else return <Redirect to='/xx'>//重定向到某个页面
}} />
children(函数或组件)
<Router path='/home' children= />
两种书写方式
1.jsx方式:精确匹配,必须和地址栏一样时才渲染 默认情况没有返回给匹配的渲染组件中this.props中注入路由对象,一般不用.
<Router path='/home' children={<Home />} />
2.回调函数方式(全匹配) 渲染不关心path和地址栏是否一致,都会渲染
<Route
path='/login'
children={ router =>{
if(router.match){
return <Home { ...router }>
}
return <div>未能匹配</div>
} } />
注意:
如果不匹配router中match则为对象 不匹配的时候为null。 根据不同的路由对象显示不同的样式
withRouter高阶组件
类组件中的使用:
- 将不是通过路由直接渲染出的组件,拥有路由对象(history,match,location...),比如入口文件渲染的App根组件。
使用方式:
1.引入:import { withRouter } from 'react-router-dom'
2.定义:export default withRouter(App)
3.使用:此时App组件的props中就拥有了路由对象,可以将此对象传递。
当然前面是类组件中的方式,在函数组件中也有类似的方式来获取。
函数组件利用Hook函数获取路由对象:
1.引入:import { useLocation, useHistory,useParams } from 'react-router-dom'
2.定义:
const location = useLocation()
const history = useHistory()
const params = useParams()
自定义导航:
作用:
因为声明式导航在编译后会变成a标签,太过于单一。不能满足需求,自定义组件,完成指定编译后生成的html标签。
准备工作:
- 我们定义好的标签需要通过props接收数据
- 利用props的类型检查检查传入的数据是否符合要求
插件: npm i prop-types -S
- 对props进行默认值的设置
const Mylink = ({ to, children, tag: Tag }) => {
const history = useHistory()
const goto = () => {
history.push(to)
}
return (
<Container>
<Route
path={to}
children={({ match }) => {
let activeClass = match ? 'active' : ''
return (
<Tag className={activeClass} onClick={goto}>
{children}
</Tag>
)
}}
/>
</Container>
)
}
// 针对于当前的组件对于props类型进行限制
Mylink.propTypes = {
to: types.string.isRequired,//字符串,必传
tag: types.string//字符串
}
// 设置默认值
Mylink.defaultProps = {
tag: 'a'//不传入此属性时默认是a标签
}
export default Mylink
使用:
<Mylink
to='/home'
tag='h1'
>首页<Mylink>