React路由

1. SPA的理解
  • 单页Web应用(single page web application,SPA)
  • 整个应用只有一个完整的页面
  • 点击页面的链接不会刷新页面,只会做页面的局部更新
  • 数据都需要通过ajax请求获取,并在前端异步展示
2. 路由的理解
1. 什么是路由?
  • 一个路由就是一个映射关系(key: value)
  • key为路径,value可能是function或component
2. 路由分类
  • 后端路由:
    • 理解:value是function,用来处理客户端提交的请求
    • 注册路由:router.get(path, function(req, res))
    • 工作过程:当node接收到一个请求时,根据请求路径找到匹配的路由,调用路由中的函数来处理请求,返回响应数据
  • 前端路由:
    • 浏览器端路由,value是component,用于展示页面内容
    • 注册路由:<Route path="/test" component={Test}>
    • 工作过程:当浏览器的path变为/test时,当前路由组件就会变为Test组件
3. react-router-dom的理解
  • react的一个插件库
  • 专门用来实现一个SPA应用
  • 基于react的项目基本都会用到此库
4. 安装react-router-dom
  • 默认版本是6,所以安装版本5的时候npm i react-router-dom@5
3. 路由的基本使用
  • 明确好界面中的导航区、展示区

  • 导航区的a标签改为Link标签

    <Link to="/xxx">Demo</Link>

  • 展示区写Route标签进行路径的匹配

    <Route path="/xxx" component={Demo}/>

  • <App>的最外侧包裹了一个<BrowserRouter><HashRouter>

index.jsx

// 引入react核心库
import React from 'react'
// 引入ReactDOM
import ReactDOM from 'react-dom'
// 引入App组件
import App from './App'
// 引入路由
import { BrowserRouter } from 'react-router-dom'

// 渲染App到页面
ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
)

App.jsx

import React, { Component } from 'react'
import { Link, Route } from 'react-router-dom'
import Home from './components/Home'
import About from './components/About'

export default class App extends Component {
  render() {
    return (
      <div>
        <div className="row">
          <div className="col-xs-offset-2 col-xs-8">
            <div className="page-header">
              <h2>React Router Demo</h2>
            </div>
          </div>
        </div>
        <div className="row">
          <div className="col-xs-2 col-xs-offset-2">
            <div className="list-group">
              {/* 原生html中,靠a链接跳转页面 */}
              {/* <a className="list-group-item" href="./about.html">About</a>
              <a className="list-group-item active" href="./home.html">Home</a> */}

              {/* React中靠路由链接实现切换组件---编写路由链接 */}
              <Link className="list-group-item" to="/about">
                About
              </Link>
              <Link className="list-group-item" to="/home">
                Home
              </Link>
            </div>
          </div>
          <div className="col-xs-6">
            <div className="panel">
              <div className="panel-body">
                {/* 注册路由*/}
                <Route path="/about" component={About} />
                <Route path="/home" component={Home} />
              </div>
            </div>
          </div>
        </div>
      </div>
    )
  }
}

还有About和Home组件就不写出来了。
在这里插入图片描述
用Header组件作为一般组件代替标题,来区分一般组件和路由组件。
Header/index.jsx

import React, { Component } from 'react'

export default class Header extends Component {
  render() {
    console.log('Header组件接收到的props是',this.props);
    return (
      <div className="page-header">
        <h2>React Router Demo</h2>
      </div>
    )
  }
}
  • 写法不同:
    • 一般组件:<Demo/>
    • 路由组件:<Route path="/demo" component={Demo}/>
  • 存放位置不同:
    • 一般组件:components
    • 路由组件:pages
  • 接收到的props不同:
    • 一般组件:写组件标签时传递了什么,就能接收到什么
    • 路由组件:接收到三个固定的属性:
      在这里插入图片描述
4. NavLink的基本使用
{/* 如果只想给NavLink加active属性,就不用添加activeClassName类名 */}
{/* 然后给类名添加样式 */}
<NavLink activeClassName="fg" className="list-group-item" to="/about">About</NavLink>
<NavLink activeClassName="fg" className="list-group-item" to="/home">Home</NavLink>

在这里插入图片描述

5. NavLink与封装NavLink
  • NavLink可以实现由链接的高亮,通过activeClassName指定样式名
  • 标签体内容是一个特殊的标签属性、
  • 通过this.props.children可以获取标签体内容
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink>

封装的MyNavLink组件

import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'

export default class MyNavLink extends Component {
  render() {
    // console.log(this.props)
    return <NavLink activeClassName="fg" className="list-group-item" {...this.props} />
  }
}

在这里插入图片描述

6. Switch的使用
  • 通常情况下,path和component是一一对应的关系
  • Switch可以提高路由匹配的效率(单一匹配)
{/* 注册路由*/}
<Switch>
  <Route path="/about" component={About} />
  <Route path="/home" component={Home} />
  <Route path="/home" component={Test} />
</Switch>

匹配到一次后,就不再匹配,大大提高效率。

7. 解决多级路径刷新页面导致样式丢失的问题
  • public/index.html 中引入样式时不写 ./,直接写 / (常用)
  • public/index.html 中引入样式时不写 ./,直接写 %PUBLIC_URL%(常用)
  • 使用HashRouter(不常用)
8. 路由的严格匹配与模糊匹配
  • 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
  • 开启严格匹配:<Route exact={true} path="/about" component={About}/>
  • 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
    在这里插入图片描述
9. Redirect的使用
  • 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由。
import { Route, Switch, Redirect } from 'react-router-dom'
  <Switch>
    <Route path="/about" component={About} />
    <Route path="/home" component={Home} />
    <Redirect to="/about" />
  </Switch>
10. 嵌套路由
  • 注册子路由要写上父路由的path值
  • 路由的匹配是按照注册路由的顺序进行的
11. 向路由组件传递参数
  • params参数
    • 路由链接(携带参数):<Link to='/demo/test/tom/18'>详情</Link>
    • 注册路由(声明接收):<Route path="/demo/test/:name/:age" component={Test}/>
    • 接收参数:const {id,title} = this.props.match.params

Home/index.jsx

import React, { Component } from 'react'
import MyNavLink from '../../components/MyNavLink'
import { Route, Switch, Redirect } from 'react-router-dom'
import News from './News'
import Message from './Message'

export default class Home extends Component {
  render() {
    return (
      <div>
        <h3>我是Home页面</h3>
        <div>
          <ul className="nav nav-tabs">
            <li>
              <MyNavLink to="/home/news">News</MyNavLink>
            </li>
            <li>
              <MyNavLink to="/home/message">Message</MyNavLink>
            </li>
          </ul>
          {/* 注册路由 */}
          <Switch>
            <Route path="/home/news" component={News} />
            <Route path="/home/message" component={Message} />
            <Redirect to="/home/news" />
          </Switch>
        </div>
      </div>
    )
  }
}

Home/Message/index.jsx

import React, { Component } from 'react'
import Detail from './Detail'
import { Link, Route } from 'react-router-dom'

export default class Message extends Component {
  state = {
    messageArr: [
      { id: '01', title: '消息1' },
      { id: '02', title: '消息2' },
      { id: '03', title: '消息3' },
    ],
  }
  render() {
    const { messageArr } = this.state
    return (
      <div>
        <ul>
          {messageArr.map((msgObj) => {
            return (
              <li key={msgObj.id}>
                {/* 向路由组件传递params参数*/}
                <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>
              </li>
            )
          })}
        </ul>
        <hr />
        {/* 声明接收params参数 */}
        <Route path="/home/message/detail/:id/:title" component={Detail} />
      </div>
    )
  }
}

Home/Message/Detail/index.jsx

import React, { Component } from 'react'

const DetailData = [
  { id: '01', content: '你好,中国' },
  { id: '02', content: '你好,未来' },
  { id: '03', content: '你好,好久不见' },
]
export default class Detail extends Component {
  render() {
    console.log(this.props)
    // 接收params参数
    const { id, title } = this.props.match.params
    const findResult = DetailData.find((detailObj) => {
      return detailObj.id === id
    })
    return (
      <div>
        <ul>
          <li>ID:{id}</li>
          <li>Title:{title}</li>
          <li>Content:{findResult.content}</li>
        </ul>
      </div>
    )
  }
}

在这里插入图片描述

  • search参数
    • 路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'>详情</Link>
    • 注册路由(无需声明,正常注册路由即可):<Route path="/demo/test" component={Test}/>
    • 接收参数:this.props.location.search
    • 注意:获取到的search是urlencoded编码字符串,需要借助querystring解析
{/* 向路由组件传递search参数 */}
<Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>

{/* search参数无需声明接收,正常注册路由即可 */}
<Route path="/home/message/detail" component={Detail} />
// 引入qs库解析接收的search参数
import qs from 'qs'
 // 接收search参数
 const { search } = this.props.location
 const {id,title} = qs.parse(search.slice(1))

在这里插入图片描述

  • state参数
    • 路由链接(携带参数):<Link to={{path:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>
    • 注册路由(无需声明,正常注册路由即可):<Route path="/demo/test" component={Test}/>
    • 接收参数:this.props.location.state
    • 注意:刷新也可以保留住参数
{/* 向路由组件传递state参数 */}
<Link to={{ pathname: '/home/message/detail', state: { id: msgObj.id, title: msgObj.title } }}>{msgObj.title}</Link>

{/* state参数无需声明接收,正常注册路由即可 */}
<Route path="/home/message/detail" component={Detail} />
   //接收state参数
const { id, title } = this.props.location.state || {}

const findResult =
  DetailData.find((detailObj) => {
    return detailObj.id === id
  }) || {}

在这里插入图片描述

12. push与replace
  • replace会替换当前历史记录
  • 不加replace,默认就是push模式,push会保留历史记录
<MyNavLink replace to="/about">About</MyNavLink>
13. 编程式路由导航

借助this.props.history对象上的API对操作路由跳转、前进、后退

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

App.jsx

import React, { Component } from 'react'
import { Route, Switch, Redirect } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import Header from './components/Header'
import MyNavLink from './components/MyNavLink'

export default class App extends Component {
  render() {
    return (
      <div>
        <div className="row">
          <div className="col-xs-offset-2 col-xs-8">
            <Header />
          </div>
        </div>
        <div className="row">
          <div className="col-xs-2 col-xs-offset-2">
            <div className="list-group">
              {/* 原生html中,靠a链接跳转页面 */}
              {/* <a className="list-group-item" href="./about.html">About</a>
              <a className="list-group-item active" href="./home.html">Home</a> */}

              {/* React中靠路由链接实现切换组件---编写路由链接 */}
              {/* 如果只想给NavLink加active属性,就不用添加activeClassName类名 */}
              <MyNavLink to="/about">About</MyNavLink>
              <MyNavLink to="/home">Home</MyNavLink>
            </div>
          </div>
          <div className="col-xs-6">
            <div className="panel">
              <div className="panel-body">
                {/* 注册路由*/}
                <Switch>
                  <Route path="/about" component={About} />
                  <Route path="/home" component={Home} />
                  <Redirect to="/about" />
                </Switch>
              </div>
            </div>
          </div>
        </div>
      </div>
    )
  }
}

index.js

// 引入react核心库
import React from 'react'
// 引入ReactDOM
import ReactDOM from 'react-dom'
// 引入App组件
import App from './App'
// 引入路由
import { BrowserRouter } from 'react-router-dom'

// 渲染App到页面
ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
)

Home/Message/index.jsx

import React, { Component } from 'react'
import Detail from './Detail'
import { Link, Route } from 'react-router-dom'

export default class Message extends Component {
  state = {
    messageArr: [
      { id: '01', title: '消息1' },
      { id: '02', title: '消息2' },
      { id: '03', title: '消息3' },
    ],
  }

  replaceShow = (id, title) => {
    // replace跳转+携带params参数
    // this.props.history.replace(`/home/message/detail/${id}/${title}`)

    // replace跳转+携带search参数
    // this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)

    // replace跳转+携带state参数
    this.props.history.replace(`/home/message/detail`, { id, title })
  }

  pushShow = (id, title) => {
    // push跳转+携带params参数
    // this.props.history.push(`/home/message/detail/${id}/${title}`)

    // push跳转+携带search参数
    // this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)

    // push跳转+携带state参数
    this.props.history.push(`/home/message/detail`, { id, title })
  }

  back = () => {
    this.props.history.goBack()
  }

  forward = () => {
    this.props.history.goForward()
  }

  go = () => {
    this.props.history.go(2)
  }

  render() {
    const { messageArr } = this.state
    return (
      <div>
        <ul>
          {messageArr.map((msgObj) => {
            return (
              <li key={msgObj.id}>
                {/* 向路由组件传递params参数*/}
                {/* <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>
                &nbsp;<button onClick={() => this.pushShow(msgObj.id, msgObj.title)}>push查看</button>
                &nbsp;<button onClick={() => this.replaceShow(msgObj.id, msgObj.title)}>replace查看</button> */}
                {/* 向路由组件传递search参数 */}
                {/* <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>
                &nbsp;<button onClick={() => this.pushShow(msgObj.id, msgObj.title)}>push查看</button>
                &nbsp;<button onClick={() => this.replaceShow(msgObj.id, msgObj.title)}>replace查看</button> */}
                {/* 向路由组件传递state参数 */}
                <Link to={{ pathname: '/home/message/detail', state: { id: msgObj.id, title: msgObj.title } }}>{msgObj.title}</Link>
                &nbsp;<button onClick={() => this.pushShow(msgObj.id, msgObj.title)}>push查看</button>
                &nbsp;<button onClick={() => this.replaceShow(msgObj.id, msgObj.title)}>replace查看</button>
              </li>
            )
          })}
        </ul>
        <hr />
        {/* 声明接收params参数 */}
        {/* <Route path="/home/message/detail/:id/:title" component={Detail} /> */}

        {/* search参数无需声明接收,正常注册路由即可 */}
        {/* <Route path="/home/message/detail" component={Detail} /> */}

        {/* state参数无需声明接收,正常注册路由即可 */}
        <Route path="/home/message/detail" component={Detail} />

        <button onClick={this.back}>回退</button>
        <button onClick={this.forward}>前进</button>
        <button onClick={this.go}>go</button>
      </div>
    )
  }
}

Home/Message/Detail/index.jsx

import React, { Component } from 'react'
// 引入qs库解析接收的search参数
// import qs from 'qs'

const DetailData = [
  { id: '01', content: '你好,中国' },
  { id: '02', content: '你好,未来' },
  { id: '03', content: '你好,好久不见' },
]
export default class Detail extends Component {
  render() {
    console.log(this.props)
    // 接收params参数
    // const { id, title } = this.props.match.params

    // 接收search参数
    // const { search } = this.props.location
    // const {id,title} = qs.parse(search.slice(1))

    //接收state参数
    const { id, title } = this.props.location.state || {}

    const findResult =
      DetailData.find((detailObj) => {
        return detailObj.id === id
      }) || {}
    return (
      <div>
        <ul>
          <li>ID:{id}</li>
          <li>Title:{title}</li>
          <li>Content:{findResult.content}</li>
        </ul>
      </div>
    )
  }
}

在这里插入图片描述

14. withRouter
  • withRouter可以加工一般组件,让一般组件具备组件所特有的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()
  }

  go = () => {
    this.props.history.go(2)
  }

  render() {
    console.log('Header组件接收到的props是', this.props)
    return (
      <div className="page-header">
        <h2>React Router Demo</h2>
        <button onClick={this.back}>回退</button>
        <button onClick={this.forward}>前进</button>
        <button onClick={this.go}>go</button>
      </div>
    )
  }
}

export default withRouter(Header)

在这里插入图片描述

16. BrowserRouter与HashRouter的区别
  • 底层原理不一样:
    • BrowserRouter使用的H5的history API,不兼容IE9及以下版本
    • HashRouter使用的是URL的哈希值
  • path表现形式不一样:
    • BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
    • HashRouter的路径中包含#,例如:localhost:3000/#/demo/test
  • 刷新后对路由state参数的影响
    • BrowserRouter没有任何影响,因为state保存在history对象中
    • HashRouter刷新后导致路由state参数的丢失
  • 注意:HashRouter可以用于解决一些路径错误相关的问题
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FG.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值