React Router 5

463 阅读6分钟

React Router 5

前端路由模式

Hash

兼容性好。 但是地址栏带#影响美观,依靠锚点和onhashchange事件实现页面的切换

History

地址栏较为美观,没有突兀的#号

缺点:

  1. 上线部署时需要结合后端的配置
  2. 兼容性略差 依赖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>
  1. to:string | object :路由跳转的地址
  2. exact:严格匹配(不常用)

NavLink标签

<NavLink 
   to='/home'
   avtiveClassName='xxx'
   exact = { true }//严格模式匹配。实例
>
   内容
</NavLink>

特点:

  1. 激活样式:点击时添加了active类名
  2. 可以自定义激活样式名avtiveClassName='xxx'

to属性的写法(对象形式):

to={{ pathname:'/home',query:{ id:xxx } }}
to={{ pathname:'/home',search:'id:xxx ' }}

编程式导航

特点:

  1. 通过js来完成导航切换,通过this.props中的history对象push/replace/goBack等方法实现编程式导航功能。
  2. 要求:只有当前组件被路由规则匹配到,才会有this.props.history对象
  3. 被匹配组件的子组件没有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标签。

准备工作:

  1. 我们定义好的标签需要通过props接收数据
  2. 利用props的类型检查检查传入的数据是否符合要求

插件: npm i prop-types -S

  1. 对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>