React-Router-Dom

本文详细介绍了React-Router-Dom的使用,包括初步编写、history模式下的问题及解决方案、路由匹配方式、Redirect用法、路由传参、search和state参数、路由嵌套以及编程路由导航。特别讨论了history模式下多级路由刷新导致样式丢失的问题,并提供了三种解决办法。此外,还对比了BrowserRouter和HashRouter的区别以及刷新后对state参数的影响。
摘要由CSDN通过智能技术生成

单页面应用(SPA), 前端路由变化不会刷新页面, 不会向服务器发送请求

路由分为前端路由和后端路由, 前前后端路由都是一个键值对,

前端(path, component), 一个路由对应一个组件

后端(path, function), 一个路由对应一个处理函数

单页面应用核心在与history, 即BOM下的history, 两种模式, 一种就是history模式, 另一种是hash模式

前端路由变化可以被监听到, 因此可以写上对应的component

初步编写

app中

import React, { Component } from 'react'
import { Route, Link, NavLink } from 'react-router-dom'
import Hello from './components/Hello'
import World from './components/World'
import './index.css'

export default class App extends Component {

  render () {
    return (
      <div>
        // Link以及Route需要包含在Router里面, 有两种Router, 一个是BrowserRouter, 一个是HashRouter
        // 分别对应history和hash模式, 可以在这里包裹, 也可以在index.js中挂载时就包裹

        /* link不用多说
        使用link不会在对应的路由自动加上类名
        <Link className="a" to="/Hello">Hello</Link><br />
        <Link className="a" to="/World">World</Link>
        */

        // 可以使用NavLink, 在对应路由会默认加上active类, 可以定义这个类对应的样式以区别
        // 也可以自定义根据路由加上什么类名, 采用activeClassName=""来定义
        <NavLink activeClassName="b" className="a" to="/Hello">Hello</NavLink><br />
        <NavLink activeClassName="b" className="a" to="/World">World</NavLink>
        // 这里不是view什么的, 就是一个路由, 有对应的(path, component)
        <Route path="/Hello" component={Hello}></Route>
        <Route path="/World" component={World}></Route>
      </div>
    )
  }
}

将router壳包在index.js中
// 入口文件就是引入 + 挂载App实例
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
// history模式
// import { BrowserRouter } from 'react-router-dom'
// hash模式
import { HashRouter } from 'react-router-dom'

// ReactDOM.render(<BrowserRouter><App /></BrowserRouter>, document.getElementById('root'))
// 也可以采用Hsah模式
ReactDOM.render(<HashRouter><App /></HashRouter>, document.getElementById('root'))


路由组件能接受到数据, 封装NavLink, 标签体也是属性(children), 直接写children就可以显示为标签体

如果有多个相同路由都会匹配的情况下, 需要包裹在Switch块中, 这样匹配到一个后就会停止不继续匹配

(默认是继续向下匹配)

<Switch>
  <Route path="/about" component={About}/>
  <Route path="/home" component={Home}/>
  <Route path="/home" component={Test}/>
</Switch>

histroy模式下, 多级路由刷新样式丢失

localhost: 3000表示的路径 === public 及public为根路径, 找不到的话会返回index.html, 只访问public(localhost: 3000)会返回index.html

原因: 会以最低一级路由的./请求, 将前面级也当做请求路径

解决1, ./中去掉 . , 直接就/去找根路径

解决2, 引入时使用%PUBLIC_URL%

解决3, 使用hash

路由匹配方式

在link中的to, 会与route中的路由进行匹配

/home 和 /home/a/b 不能匹配(link ==> route精准匹配, 不能给错, 不能给少)

/home/a/b 和/home 匹配(link ==> route模糊匹配, 可以给多, 不能给错, 先下面那样)

默认是模糊匹配,按顺序比对, 开始不匹配后面就不会匹配了, 如/a/home/b不匹配

注册路由时, **exact = {true}**开启精准匹配, 或直接exact即可, 一般不开启

Redirect

与vue有些许差别, 这个是用来兜底的, 表示都没匹配到的时候, 去某个路由

 <div>
    <MyLink className="a" to="/Hello">Hello</MyLink><br />
    <MyLink className="a" to="/World">World</MyLink>
    <Route path="/Hello" component={Hello} />
    <Route path="/World" component={World} />
    {/* 当匹配不到任何路由时, 可以用这个兜底, 这是用的是to不是path */}
    <Redirect to="/Hello" />
  </div>

路由传参

params

link中传递的参数路由在注册时需要接收

注册路由时冒号声明后才能接收, 值在路由组件props中的match中parms中

<MyLink to={`/home/message/${message[0].id}/${message[0].tittle}`}>message001</MyLink>
<MyLink to={`/home/message/${message[1].id}/${message[1].tittle}`}>message002</MyLink>
<MyLink to={`/home/message/${message[2].id}/${message[2].tittle}`}>message003</MyLink>
{/* 上面既然传值了, 这里就匹配路由的时候就需要接收 */}
<Route path="/home/message/:id/:tittle" component={Detil} />

在Detil路由组件中取值

render () {
    const { id, tittle } = this.props.match.params
    console.log(this.props)
    return (
      <div>
        <h3>这是第{id}</h3>
        <h3>{tittle}</h3>
      </div>
    )
  }

search参数

也可以采用?的方式进行search方式传参(query参数)

传参

<MyLink to={`/home/message/?id=${message[0].id}&tittle=${message[0].tittle}`}>message001</MyLink>

接收是不需要声明(传递的时候已经声明了)

传过去的值在组件对象里的props.location.search中, 但是这是urlencode编码的形式

需要借助querystring库(不需要安装)

import qs from 'querystring'
qs.stringify(对象) // 将一个对象转化为urlencode编码的格式
qs.parse(Str) // 将一个urlencode格式字符串转化为一个对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GGt2ixpw-1628613664309)(https://secure.wostatic.cn/static/3XWnXbRZxmMUvK81PE7wBV/image.png)]

state参数

声明路由时可以为一个对象, 里面的路由key是pathname, 利用这个可以传递state对象

<MyLink to={{ pathname: '/home/message', state: { id: 1, tittle: '我是1消息' } }}>message001</MyLink>

注册路由同样不需要声明接收

传过去的值在组件对象里的props.location.state中, url上不展示, 即使刷新了也还存在, 因为histroy对象在维护location, location虽然是props的属性, 但是也是histroy的属性

注意如果清空缓存, 会丢失, 所以接收时可以写成 xxx = props.location.state || {}

路由嵌套

根路由组件

<div>
  <MyLink className="a" to="/About">About</MyLink><br />
  <MyLink className="a" to="/Home">Home</MyLink>
  <Route path="/About" component={About} />
  {/* 精准匹配模式不能随便开启, 这种当有子路由并且子路由重定向时, 就会匹配不到子路由
  因为这里点击home后, 此时的匹配模式是home/message(因为home路由下子路由的重定向)
  但是此时home组件里的路由还未注册, 只能在这里匹配, 这里只能匹配到home, 但是因为精准匹配
  home不会被匹配 */}
  <Route path="/Home" component={Home} />
  {/* 当匹配不到任何路由时, 可以用这个兜底 */}
  <Redirect to="/About" />
</div>

一级子路由组件

  render () {
    return (
      <div>
        <h3>我是Home的内容</h3>
        <div>
          <ul className="nav nav-tabs">
            <li>
              <MyLink to="/home/news">news</MyLink>
            </li>
            <li>
              <MyLink to="/home/message">message</MyLink>
            </li>
          </ul>
          {/* 注册路由 */}
          {/* 嵌套路由需要写全,上一级的路由也要带上 */}
          <Route path="/home/message" component={Message} />
          <Route path="/home/news" component={News} />
          <Redirect to="/home/message" />
        </div>
      </div>
    )

编程路由导航

history下的push和replace方法, 两个参数, 一个path, 一个是state参数

二者方法使用上是一致的, 只是压栈的方式不同, 这里只列举一个

// 点击后进行路由变化
<button onClick={() => { this.toThree(message[2].id, message[2].tittle) }}>消息3</button>

// 定义的函数
  toThree = (id, tittle) => {
    // 传递search参数
    // this.props.history.push(`/home/message/?id=${id}$tittle=${tittle}`)
    // 传递params参数, 需要声明接收
    // this.props.history.push(`/home/message/${id}/${tittle}`)
    // 传递state参数, 第二个参数就是用来传递state的
    // this.props.history.push('/home/message', { id, tittle })
  }

withRouter, 一般组件用路由组件的方法, 暴露withRouter(Header)

import React, { Component } from 'react'
// 一般组件是不能使用history对象的方法的, 也就是不能对路由进行操作
// 使用的话需要借助react-router-dom下的withRouter, 并且将这个组件向外暴露为处理过的
import { withRouter } from 'react-router-dom'

// 不能这么暴露组件, 需要暴露一个被withRouter处理过后的组件才能操作路由
// export default class index extends Component {
//   goback = () => {
//     this.props.history.goBack()
//   }
//   render () {
//     return (
//       <div>
//         <button onClick={this.goback}>后退</button>
//       </div>
//     )
//   }
// }
class Header extends Component {
  goback = () => {
    this.props.history.goBack()
  }
  render () {
    return (
      <div>
        <button onClick={this.goback}>后退</button>
      </div>
    )
  }
}
export default withRouter(Header)

路由总结

vue有单独的路由配置文件, react目前了解到的还没有, 对路由的操作, 编程式也好, 声明式也好, 都是在组件内定义的

都是都是react的history对象对路由进行操作的

BrowserRouter是根据H5的history(不同于react路由下的history)的api进行封装的

HashRouter 使用的是url的哈希值

后者在state传参下, 刷新时会丢失数据

  1. 底层原理不一样:

BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。

HashRouter使用的是URL的哈希值。

  1. path表现形式不一样

BrowserRouter的路径中没有#,例如:localhost:3000/demo/test

HashRouter的路径包含#,例如:localhost:3000/#/demo/test

  1. 刷新后对路由state参数的影响

(1).BrowserRouter没有任何影响,因为state保存在history对象中。

(2).HashRouter刷新后会导致路由state参数的丢失!!!

  1. 备注:HashRouter可以用于解决一些路径错误相关的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值