第三十二章 React路由组件的简单使用

简介: 第三十二章 React路由组件的简单使用

1、NavLink的使用

一个特殊版本的 Link,当它与当前 URL 匹配时,为其渲染元素添加样式属性

<NavLink className="list-group-item" to="/home">Home</NavLink>
<NavLink className="list-group-item" to="/about">About</NavLink>

以上代码有一个默认的特殊效果,就是当你选中该路由时会有一个默认的激活样式,默认的类名:active

如果你要修改该激活样式,可以自定义样式:

.active1{
    background-color: orange !important;
    color: white !important;
}

添加一个属性activeClassName将类名active1填进去:

<NavLink className="list-group-item" activeClassName="active1" to="/home">Home</NavLink> <NavLink className="list-group-item" activeClassName="active1" to="/about">About</NavLink>

image.png

由于这里代码有些冗长,我们简单封装一下:

封装组件MyNavLink

import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
export default class MyNavLink extends Component {
  render() {
    return (
      <NavLink className="list-group-item" activeClassName="active1" {...this.props}/>
    )
  }
}

导入组件MyNavLink

import MyNavLink from './components/MyNavLink'

使用组件MyNavLink

<MyNavLink to="/home"  children="Home"/> 
<MyNavLink to="/about" children="About"/>

将标签属性tochildren传入组件,其中to属性的内容就是路由路径,children就是标签体内容。

小总结

  • NavLink可以实现路由链接的高亮,通过activeClassName指定样式类名
  • 标签体内容是一个特殊的标签属性
  • 通过this.props.children可以获取标签体内容

2、Switch的使用

渲染与该地址匹配的第一个子节点<Route>或者<Redirect>

import { Switch, Route } from 'react-router'
<Switch>
  <Route exact path="/" component={Home}/>
  <Route path="/about" component={About}/>
  <Route path="/:user" component={User}/>
  <Route component={NoMatch}/>
</Switch>
  • 通常情况下pathcomponent是一一对应的关系
  • Switch可以提高路由匹配的效率(单一匹配)

解决多级路径刷新样式丢失的问题

  • public/index.html 中 引入样式时不写.// (常用)
  • public/index.html 中 引入样式时不写 ./ %PUBLIC_URL% (常用)
  • 使用HashRouter

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

  • 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
  • 开启严格匹配:<Route exact={true} path="/about" component={About}/>
  • 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

3、Redirect组件的使用

页面重定向:一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由

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

详情参见:React路由之Redirect


4、路由嵌套

News组件:

import React, { Component } from 'react'
export default class News extends Component {
  render() {
    return (
      <ul>
        <li>news001</li>
        <li>news002</li>
        <li>news003</li>
      </ul>
    )
  }
}

Message组件:

import React, { Component } from 'react'
export default class Message extends Component {
  render() {
    return (
      <div>
        <ul>
          <li>
            <a href="/message1">message001</a>&nbsp;&nbsp;
          </li>
          <li>
            <a href="/message2">message002</a>&nbsp;&nbsp;
          </li>
          <li>
            <a href="/message/3">message003</a>&nbsp;&nbsp;
          </li>
        </ul>
      </div>
    )
  }
}

Home组件:

import React, { Component } from 'react'
import { Switch, Route, Redirect } from 'react-router-dom'
import MyNavLink from '../MyNavLink'
import News from './News'
import Message from './Message'
export default class Home extends Component {
  render() {
    // console.log('这是Home组件的Props',this.props)
    return (
      <div>
        <h3>我是Home的内容</h3>
        <div>
          <ul className="nav nav-tabs">
            <li>
              {/* 注册子路由时要写上父路由的path值 */}
              <MyNavLink to="/home/news">News</MyNavLink>
            </li>
            <li>
              <MyNavLink to="/home/message">Message</MyNavLink>
            </li>
          </ul>
          {/* 注册路由 */}
          <Switch>
            {/* 注册子路由时要写上父路由的path值 */}
            <Route path="/home/news" component={News} />
            <Route path="/home/message" component={Message} />
            <Redirect to="/home/news" />
          </Switch>
        </div>
      </div>
    )
  }
}

整体效果:

image.png

注意点:

1.注册子路由时要写上父路由的path

2.路由的匹配是按照注册路由的顺序进行的

5、向路由组件传递params参数

组件目录结构

src
|--components
|        |--Home
|             |--News
|             |--Message
|                 |--Detail
|        |--About
|        |--Header
|--App.jsx
|--index.js

主要组件Message

import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
  state = {
    msgArr: [
      { id: '01', title: 'msg-01' },
      { id: '02', title: 'msg-02' },
      { id: '03', title: 'msg-03' },
    ],
  }
  render() {
    const { msgArr } = this.state
    return (
      <div>
        <ul>
          {msgArr.map((msgObj) => {
            return (
              <li key={msgObj.id}>
                <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>&nbsp;&nbsp;
              </li>
            )
          })}
        </ul>
        <hr />
        <Route path="/home/message/detail/:id/:title" component={Detail}/>
      </div>
    )
  }
}

URL匹配路径/home/message/:id/:title时渲染的组件。:id是一个参数,可以通过props.match.params.id访问。:title也是一样。

主要组件Detail

import React, { Component } from 'react'
export default class Detail extends Component {
  state = {
    info:[
      {id:'01',content:'你好我是信息1'},
      {id:'02',content:'你好我是信息2'},
      {id:'03',content:'你好我是信息3'},
    ]
  }
  render() {
    console.log(this.props)
    const {info} = this.state
    const {id,title} = this.props.match.params
    const res = info.find(infoObj=>{
      return infoObj.id === id
    })
    return (
      <ul>
        <li>ID:{id}</li> 
        <li>TITLE:{title}</li> 
        <li>CONTENT:{res.content}</li> 
      </ul>
    )
  }
}

在这里我们通过解构this.props.match.params获得参数idtitle。以下是我们打印的标签属性:

history: Object { length: 44, action: "PUSH", location: {…}, … }
location: Object { pathname: "/home/message/detail/01/msg-01", search: "", key: "5ugqcd", … }
match: 
    isExact: true
    params: Object { id: "01", title: "msg-01" }
    id: "01"
    title: "msg-01"
    path: "/home/message/:id/:title"
    url: "/home/message/01/msg-01"

从以上打印结果中,我们可以清晰的看到我们的参数位置:match.params

整体效果

image.png

小总结

路由链接(携带参数):<link to='/demo/test/tom/19'>详情</Link>

注册路由(声明接收):<Route path='/demo/test/:name/:age' component={Test} />

接收参数:const {name,age} = this.props.match.params

6、向路由组件传递search参数

修改Message组件

import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
  state = {
    msgArr: [
      { id: '01', title: 'msg-01' },
      { id: '02', title: 'msg-02' },
      { id: '03', title: 'msg-03' },
    ],
  }
  render() {
    const { msgArr } = this.state
    return (
      <div>
        <ul>
          {msgArr.map((msgObj) => {
            return (
              <li key={msgObj.id}>
                {/* 传递params参数 */}
                {/* <Link to={`/home/message/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>&nbsp;&nbsp; */}
                {/* 传递search参数 */}
                <Link to={`/home/message/detail?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>&nbsp;&nbsp;
              </li>
            )
          })}
        </ul>
        <hr />
        <Route path="/home/message/detail" component={Detail}/>
      </div>
    )
  }
}

这里的路由链接的参数需要使用?key=value&key=value的格式,也叫urlencode格式。路由注册照常即可,不需要声明参数。

修改Detail组件

import React, { Component } from 'react'
import qs from 'qs'
export default class Detail extends Component {
  state = {
    info:[
      {id:'01',content:'你好我是信息1'},
      {id:'02',content:'你好我是信息2'},
      {id:'03',content:'你好我是信息3'},
    ]
  }
  render() {
    console.log(this.props)
    const {info} = this.state
    const {search} = this.props.location
    const {id,title} = qs.parse(search.slice(1))
    const res = info.find(infoObj=>{
      return infoObj.id === id
    })
    return (
      <ul>
        <li>ID:{id}</li> 
        <li>TITLE:{title}</li> 
        <li>CONTENT:{res.content}</li> 
      </ul>
    )
  }
}

这里引入了一个库qs,无需安装,直接引入即可,它有两个方法可以使得urlencode格式的数据与object格式的数据进行转换。

  • qs.stringify(): 将{a:'1',b:2}转换>a=1&b=2
  • qs.parse(): 将a=1&b=2转换>{a:"1",b:"2"}

查看标签属性:

history: Object { length: 7, action: "PUSH", location: {…}, … }
location: 
        hash: ""
        key: "0wurd0"
        pathname: "/home/message/detail"
        search: "?id=01&title=msg-01"
        state: undefined
match: Object { path: "/home/message/detail", url: "/home/message/detail/", isExact: true, … }

我们可以看到search参数是一个字符串在location对象里面,所以我们只需要将它解构取出值,即可进行后续操作。

小总结

路由链接(携带参数):<link to='/demo/test?name=tom&age=19'>详情</Link>

注册路由(无需声明,正常注册即可):<Route path='/demo/test' component={Test} />

接收参数:const {search} = this.props.location

解析参数:获取到的search是urlencode编码字符串,需要借助qs解析。

7、向路由组件传递state参数

修改Message组件

import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
  state = {
    msgArr: [
      { id: '01', title: 'msg-01' },
      { id: '02', title: 'msg-02' },
      { id: '03', title: 'msg-03' },
    ],
  }
  render() {
    const { msgArr } = this.state
    return (
      <div>
        <ul>
          {msgArr.map((msgObj) => {
            return (
              <li key={msgObj.id}>
                {/* 传递params参数 */}
                {/* <Link to={`/home/message/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>&nbsp;&nbsp; */}
                {/* 传递search参数 */}
                {/* <Link to={`/home/message/detail?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>&nbsp;&nbsp; */}
                {/* 传递state参数 */}
                <Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>&nbsp;&nbsp;
              </li>
            )
          })}
        </ul>
        <hr />
        <Route path="/home/message/detail" component={Detail}/>
      </div>
    )
  }
}

对象的形式state参数放在路由链接里面。

修改的Detail组件

import React, { Component } from 'react'
// import qs from 'qs'
export default class Detail extends Component {
  state = {
    info:[
      {id:'01',content:'你好我是信息1'},
      {id:'02',content:'你好我是信息2'},
      {id:'03',content:'你好我是信息3'},
    ]
  }
  render() {
    console.log(this.props)
    const {info} = this.state
    const {id,title} = this.props.location.state || {}
    // const {id,title} = qs.parse(search.slice(1))
    const res = info.find(infoObj=>{
      return infoObj.id === id
    }) || {}
    return (
      <ul>
        <li>ID:{id}</li> 
        <li>TITLE:{title}</li> 
        <li>CONTENT:{res.content}</li> 
      </ul>
    )
  }
}

获取state参数也是在location里面获取,我们可以看看标签属性的结构:

history: Object { length: 11, action: "REPLACE", location: {…}, … }
location: 
        hash: ""
        key: "o47hxa"
        pathname: "/home/message/detail"
        search: ""
        state: Object { id: "01", title: "msg-01" }
match: Object { path: "/home/message/detail", url: "/home/message/detail", isExact: true, … }

我们可以从打印结果里面清晰的看到state的参数

小总结

路由链接(携带参数):<link to={{pathname:'/demo/test',state:{name:'tom',age:19}}}>详情</Link>

注册路由(无需声明,正常注册即可):<Route path='/demo/test' component={Test} />

接收参数:const {state} = this.props.location

解析参数:刷新也可以保留住参数。

8、编程式路由导航

传递params参数
  1. 注册事件
<button onClick={() => this.pushRoute(msgObj.id, msgObj.title)}>push路由</button>
<button onClick={() => this.replaceRoute(msgObj.id, msgObj.title)}>replace路由</button>
  1. 事件逻辑
  pushRoute = (id, title) => {
    // push路由跳转===>params参数
    this.props.history.push(`/home/message/detail/${id}/${title}`)
  }
  replaceRoute = (id, title) => {
    // replace路由跳转===>params参数
    this.props.history.replace(`/home/message/detail/${id}/${title}`)
  }
  1. 注册路由
<Route path="/home/message/detail/:id/:title" component={Detail} />
  1. 接收参数
 const {id,title} = this.props.match.params
传递search参数
  1. 注册事件
<button onClick={() => this.pushRoute(msgObj.id, msgObj.title)}>push路由</button>
<button onClick={() => this.replaceRoute(msgObj.id, msgObj.title)}>replace路由</button>
  1. 事件逻辑
  pushRoute = (id, title) => {
    // push路由跳转===>search参数
    this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)
  }
  replaceRoute = (id, title) => {
    // replace路由跳转===>search参数
    this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)
  }
  1. 注册路由
<Route path="/home/message/detail" component={Detail} />
  1. 接收参数
const {search} = this.props.location
const {id,title} = qs.parse(search.slice(1))
传递state参数
  1. 注册事件
<button onClick={() => this.pushRoute(msgObj.id, msgObj.title)}>push路由</button>
<button onClick={() => this.replaceRoute(msgObj.id, msgObj.title)}>replace路由</button>
  1. 事件逻辑
  pushRoute = (id, title) => {
    // push路由跳转===>state参数
    this.props.history.push('/home/message/detail', { id, title })
  }
  replaceRoute = (id, title) => {
    // replace路由跳转
    this.props.history.replace('/home/message/detail', { id, title })
  }
  1. 注册路由
<Route path="/home/message/detail" component={Detail} />
  1. 接收参数
const {id,title} = this.props.location.state || {}

小总结

编程式路由导航就是借助this.props.history身上的几个API进行路由的前进、后退、跳转、替换。

  • this.props.history.go()
  • this.props.history.goBack()
  • this.props.history.goForward()
  • this.props.history.push()
  • this.props.history.replace()

9、withRouter的使用

我们知道一般组件,如果不传标签属性,接收的props是一个空对象,是没有那些路由属性和API的,那么一般组件里面如何使用路由进行跳转呢?那就要使用withRouter对一般组件进行改造了。

import React, { Component } from 'react'
import {withRouter} from 'react-router-dom'
 class Header extends Component {
  back = () => {
    this.props.history.goBack()
  }
  forward = () => {
    this.props.history.goForward()
  }
  go1 = () => {
    this.props.history.go(-2)
  }
  render() {
    console.log('这是Header组件的Props',this.props)
    return (
      <div className="col-xs-offset-2 col-xs-8">
        <div className="page-header"><h2>React Router Demo</h2></div>
        <button onClick={this.back}>后退</button>
        <button onClick={this.forward}>前进</button>
        <button onClick={this.go1}>go</button>
      </div>
    )
  }
}
export default withRouter(Header)

以上是对一般组件Header进行改造,使得它拥有路由的属性和API,这样我们就可以使用路由的前进、后退、跳转等API操作路由了。

小总结

  • withRouter可以加工一般组件,使得它拥有路由组件的属性和API
  • withRouter的返回值是一个新的组件

10、BrowserRouter与HashRouter的区别

1、底层原理不一样

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

HashRouter使用的是URL的哈希值

2、path表现形式不一样

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

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

3、刷新后对路由state参数的影响

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

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

4、备注:HashRouter可以用于解决一些路径错误相关的问题

相关文章
|
1天前
|
移动开发 前端开发 JavaScript
React 视频播放控制组件 Video Controls
本文深入探讨了如何使用React创建功能丰富的视频播放控制组件(Video Controls)。首先介绍了React与HTML5 `&lt;video&gt;` 标签的基础知识,展示了如何通过状态管理和事件处理实现自定义控件。接着分析了常见问题如视频加载失败、控件样式不一致、状态管理混乱和性能问题,并提供了相应的解决方案。最后通过完整代码案例详细解释了播放、暂停、进度条和音量控制的实现方法,帮助开发者在React中构建高质量的视频播放组件。
36 17
|
6天前
|
移动开发 前端开发 UED
React 音频预览组件:Audio Preview
本文介绍如何使用 React 构建音频预览组件,涵盖基础实现、常见问题及解决方案。通过 HTML5 `&lt;audio&gt;` 标签和 React 状态管理,实现播放控制。解决文件路径、浏览器兼容性等问题,并优化性能,避免状态不同步和内存泄漏,提升用户体验。
54 22
|
8天前
|
移动开发 前端开发 UED
React 音频播放器组件 Audio Player
本文介绍如何使用React创建功能丰富的音频播放器组件。基于HTML5 `&lt;audio&gt;` 标签,结合React的状态管理和事件处理,实现播放、暂停、进度和音量控制等功能。通过代码示例展示基本实现,并探讨常见问题如自动播放限制、进度条不更新、文件加载失败及多实例冲突的解决方案。同时,避免易错点如忽略生命周期管理、错误处理和性能优化,确保高效开发与良好用户体验。
62 23
|
10天前
|
缓存 前端开发 JavaScript
React 视频弹幕组件 Video Danmaku
本文介绍了如何在React中构建视频弹幕组件,提升用户观看体验和互动性。首先通过Create React App初始化项目,并集成`react-player`作为视频播放器。接着实现基本弹幕功能,包括评论的接收与显示,使用CSS动画实现滚动效果。针对常见问题如弹幕重叠、性能下降及同步问题,提供了随机化位置、分批加载和监听播放进度等解决方案。最后探讨了弹幕分类和特效等高级技巧,确保弹幕系统的高性能和良好用户体验。
52 23
|
11天前
|
移动开发 前端开发 JavaScript
React 视频播放控制组件 Video Controls
本文介绍了如何使用 React 构建视频播放控制组件(Video Controls),涵盖基本概念、创建步骤和常见问题解决。首先,通过 HTML5 `&lt;video&gt;` 标签和 React 组件化思想,实现播放/暂停按钮和进度条等基础功能。接着,详细讲解了初始化项目、构建 `VideoControls` 组件及与主应用的集成方法。最后,针对视频无法播放、控制器状态不同步、进度条卡顿和音量控制失效等问题提供了具体解决方案,并介绍了全屏播放和自定义样式等进阶功能。希望这些内容能帮助你在实际项目中更好地实现和优化视频播放功能。
83 40
|
25天前
|
存储 编解码 前端开发
React 视频上传组件 Video Upload
随着互联网的发展,视频内容在网站和应用中愈发重要。本文探讨如何使用React构建高效、可靠的视频上传组件,涵盖基础概念、常见问题及解决方案。通过React的虚拟DOM和组件化开发模式,实现文件选择、进度显示、格式验证等功能,并解决跨域请求、并发上传等易错点。提供完整代码案例,确保用户能顺畅上传视频。
128 92
|
26天前
|
移动开发 前端开发 JavaScript
React 视频播放器组件:Video Player
本文介绍了如何使用 React 和 HTML5 `&lt;video&gt;` 标签构建自定义视频播放器组件。首先,通过创建基础的 React 项目和 VideoPlayer 组件,实现了基本的播放、暂停功能。接着,探讨了常见问题如视频加载失败、控制条样式不一致、性能优化不足及状态管理混乱,并提供了相应的解决方案。最后,总结了构建高效视频播放器的关键要点,帮助开发者应对实际开发中的挑战。
110 27
|
27天前
|
前端开发 UED 索引
React 图片灯箱组件 Image Lightbox
图片灯箱组件是一种常见的Web交互模式,用户点击缩略图后弹出全屏窗口展示大图,并提供导航功能。本文介绍了基于React框架的图片灯箱组件开发,涵盖初始化状态管理、图片加载与预加载、键盘和鼠标事件处理等常见问题及解决方案。通过`useState`和`useEffect`钩子管理状态,使用懒加载和预加载优化性能,确保流畅的用户体验。代码案例展示了组件的基本功能实现,包括打开/关闭灯箱、切换图片及键盘操作。
127 80
|
28天前
|
存储 前端开发 索引
React 图片轮播组件 Image Carousel
本文介绍了如何使用React创建图片轮播组件。首先,解释了图片轮播的基本概念和组件结构,包括图片容器、导航按钮、指示器和自动播放功能。接着,通过代码示例详细说明了创建基本组件、添加自动播放、处理边界情况的步骤。还探讨了常见问题如状态更新不及时、内存泄漏和样式问题,并提供了解决方案。最后,介绍了进阶优化,如添加过渡效果、支持触摸事件和动态加载图片,帮助读者构建更完善的轮播组件。
52 16
|
29天前
|
前端开发 JavaScript API
React 图片放大组件 Image Zoom
本文介绍如何使用React创建图片放大组件(Image Zoom),提升用户体验。组件通过鼠标悬停或点击触发放大效果,利用`useState`管理状态,CSS实现视觉效果。常见问题包括图片失真、性能下降和移动端支持,分别可通过高质量图片源、优化事件处理和添加触摸事件解决。易错点涉及状态管理混乱、样式冲突和过多事件绑定,建议使用上下文API、CSS模块及优化事件绑定逻辑。高级功能扩展如多张图片支持和自定义放大区域进一步丰富了组件的实用性。
58 25