React-router-dom 路由

2,473 阅读3分钟

路由单页面应用(SPA)的优点?

  • 整个应用只有一个完整的页面,多组件
  • 点击页面中的链接不会刷新页面,只会页面局部刷新
  • 数据通过Ajax请求,在页面异步展示

路由的理解

  • 一个路由就是一个映射关系(key: value)
  • key是路径,value可能是function(node)或component

React-router-dom API介绍

HashRouter 和 BrowserRouter

路由器,用来全局包裹路由和路由链接

<HashRouter>
	<app/>
</HashRouter>

两者区别:

  • 底层原理不一样:

    BrowserRouter 使用的h5 的API,不兼容IE9以下

    HashRouter 使用的是URL的hash值

  • url 的形式不一样

    BrowserRouter 路径中没有 #

    HashRouter 路径中包含 #

  • 刷新后对路由state参数有影响

    BrowserRouter 没有任何影响,因为state存在history的对象中

    HashRouter 刷新页面后导致state参数消失

声明式路由

Link

靠路由链接实现组件切换 -- 编写路由

跳转模式:

  • push
  • replace
<BrowserRouter>
    <Link to="/home" replace>Home</Link>
    <Link to="/about">About</Link>
</BrowserRouter>

link 相当于 a 链接标签

Route

注册路由,也就是展示路由的地方

<Route path='/home' exact component={Home}></Route>
<Route path='/about' component={About}></Route>
<Route path='/refExc' component={RefExc}></Route>

路由组件 和 一般组件

路由组件

靠路由匹配展示组件界面

一般组件

两者最大的区别传递信息:

  • 一般组件通过 props
  • 路由组件 传递消息的三个属性 history location match

NavLink

当它匹配当前的URL时,它会处于激活状态的链接样式。

<NavLink to="/about">About</NavLink>

封装一个 MyNavLink:

import {NavLink} from 'react-router-dom'

function MyNavLink(props) {
  return (
    <NavLink 
      activeStyle={{backgroundColor: '#f60', color: '#fff'}} 
      {...props}>{props.children}
    </NavLink>
  )
}

export default MyNavLink

Switch

从上往下只匹配一个符合 path 的路由,匹配到之后,下面的不在匹配。

注意:超过两个以上的路由使用。

路由的模糊匹配和严格匹配

模糊匹配

<>
    <div>
        <MyNavLink to="/home">Home</MyNavLink>
        <MyNavLink to="/about/a/b">About</MyNavLink> // 这样写的时候还是能匹配到 /about 
        <MyNavLink to="/refExc">refExc</MyNavLink>
    </div>
    <div>
        <Switch>
            <Suspense fallback={<Loading />}>
                <Route path='/home' exact component={Home}></Route>
                <Route path='/about' component={About}></Route>
                <Route path='/refExc' component={RefExc}></Route>
                <Redirect to="/home" />
            </Suspense>
        </Switch>
    </div>
</>

严格匹配

<Route exact path='/about' component={About}></Route>

添加关键字,exact 之后,只有当 path 完全相同时,才能匹配路由,开启之后将无法匹配二级路由

Redirect 重定向

语法:<Redirect to="/home" />

<Switch>
    <Suspense fallback={<Loading />}>
        <Route path='/home' exact component={Home}></Route>
        <Route exact path='/about' component={About}></Route>
        <Route path='/refExc' component={RefExc}></Route>
        <Redirect to="/home" />
    </Suspense>
</Switch>

当没有匹配到响应理由时,就会根据 Redirect 去到响应的路由。

嵌套路由

二级路由展示:

import { lazy } from 'react'
import {Route, Switch, Redirect} from 'react-router-dom'
import MyNavLink from '../../components/MyNavLink'

const News = lazy(() => import('./news'))  // 路由懒加载
const Message = lazy(() => import('./message'))

function Router() {
  return (
    <>
      <MyNavLink to="/router/news">News</MyNavLink>
      <MyNavLink to="/router/message">message</MyNavLink>

      <Switch>
        <Route path="/router/news" component={News} />
        <Route path="/router/message" component={Message} />
        <Redirect to="/router/news" />
      </Switch>
    </>
  )
}

export default Router

/router/news 先对以及路由 router 进行模糊匹配,然后在对二级路由匹配。一级路由不能开启严格模式

路由传参

params 传参

地址栏上直接以参数的形式展示

传递:

<MyNavLink to={`/router/news/${user.id}/${user.name}`}>News</MyNavLink>

声明接收参数 id和name:

<Route path="/router/news/:id/:name" component={News} />

接收:match取出params中的参数

import { Fragment } from "react";

function News(props) {
  const {id, name} = props.match.params
  return (
    <Fragment>
      <h2>我是新闻</h2>
      <p>{id}---{name}</p>
    </Fragment>
  )
}

export default News

search 参数

地址栏上以 ?id=01&name=zhangsan的形式

传递:

 <MyNavLink to={`/router/message?id=${user.id}&name=${user.name}`}>message</MyNavLink>

Route不需要接收

接收:

search在props.location中

import qs from 'querystring'

function Message(props) {
  const {search} = props.location
  const {id, name} = qs.parse(search.slice(1))
  return (
    <>
      <h2>我是消息</h2>
      <p>{id}---{name}</p>
    </>
  )
}

export default Message

state 参数

地址栏上没有展示,但是刷新页面参数不丢失

传递:

<MyNavLink to={{pathname: '/router/chat', state:{id: user.id, name: user.name}}}>chat</MyNavLink>

Route 不需要处理

接收:

function Chat(props) {
  const {id, name} = props.location.state || {}
  return (
    <>
      <h2>我是微信</h2>
      <p>{id}---{name}</p>
    </>
  )
}

export default Chat

编程式路由导航

push 和 replace 常用两种模式:

  • push:一种进栈出栈的模式,可以返回上一个路由
  • replace:一种替换路由的方式,不能返回

使用编程式路由跳转并传递参数:

import { useState, lazy } from 'react'
import {Route, Switch} from 'react-router-dom'

const Message = lazy(() => import('./message'))
const Chat = lazy(() => import('./chat'))

function Router(props) {
  const [user] = useState(
    {id: 1, name: 'zhangsan'}
  )
  
  // search参数形式
  const pushMessage = (id, name) => {
    props.history.push(`/router/message?id=${id}&name=${name}`)
  }

  // state 参数形式
  const repalceChat = (id, name) => {
    props.history.replace('/router/chat', {id, name})
  } 

  return (
    <>
      <button onClick={() => pushMessage(user.id, user.name)}>push进入message</button>
      <button onClick={() => repalceChat(user.id, user.name)}>replace进入chat</button>

      <Switch>
        <Route path="/router/message" component={Message} />
        <Route path="/router/chat" component={Chat} />
      </Switch>
    </>
  )
}

export default Router

扩展

withRouter

React 分为一般组件和路由组件,在一般组件的props中并没有history,所以一般使用 withRouter 包裹组件,使组件的props中拥有history。