React 路由

React 路由

尚硅谷 2021 版 React 技术全家桶全套完整版(零基础入门到精通/男神天禹老师亲授)

路由 SPA

  1. 单页 Web 应用(Single Page Web Application)
  2. 整个应用只有一个完整的页面
  3. 点击页面中的链接不会刷新页面,只会做页面的局部更新
  4. 数据都需要通过 ajax 请求获取,并在前端异步展现

1. 什么是路由

  1. 一个路由就是一个映射关系(key:value)
  2. key 为路径,value 可能是 function 或 component

2. 路由分类

  1. 后端路由
    • 理解:value 是 function,用来处理客户端提交的请求
    • 注册路由:router.get(path, function(req, res))
    • 工作过程:当 node 接收到一个请求时,根据请求路径找到匹配的路由,调用路由中的函数来处理请求,返回响应数据
  2. 前端路由
    • 理解:value 是 component,用于展示页面内容
    • 注册路由:<Route path="/test" component={Test}>
    • 工作流程:当浏览器的 path 变为 /test 时,当前路由组件就会变为 Test 组件
<a href="http://www.baidu.com" onclick="return push('/test1') ">push test1</a><br /><br />
<button onClick="push('/test2')">push test2</button><br /><br />
<button onClick="replace('/test3')">replace test3</button><br /><br />
<button onClick="back()">&lt;= 回退</button>
<button onClick="forword()">前进 =&gt;</button>

<script type="text/javascript" src="https://cdn.bootcss.com/history/4.7.2/history.js"></script>
<script type="text/javascript">
  // let history = History.createBrowserHistory() //方法一,直接使用H5推出的history身上的API
  let history = History.createHashHistory() //方法二,hash值(锚点)
  function push(path) {
    history.push(path)
    return false
  }
  function replace(path) {
    history.replace(path)
  }
  function back() {
    history.goBack()
  }
  function forword() {
    history.goForward()
  }
  history.listen(location => {
    console.log('请求路由路径变化了', location)
  })
</script>

样式丢失问题:

  1. public/index.html 中引入样式时不写 .// (常用)
  2. public/index.html 中引入样式时不写 ./%PUBLIC_URL% (常用)
  3. 使用 HashRouter
<link rel="stylesheet" href="./bootstrap.css" />

<!-- 1.去掉. -->
<link rel="stylesheet" href="/bootstrap.css" />
<!-- 2.使用%PUBLIC_URL% -->
<link rel="stylesheet" href="%PUBLIC_URL%/bootstrap.css" />
<!-- 3.把BrowserRouter改为HashRouter -->
<BrowserRouter>
  <App />
</BrowserRouter>
<HashRouter>
  <App />
</HashRouter>

路由基本使用

react-router-dom

  1. react 的一个插件库
  2. 专门用来实现一个 SPA 应用
  3. 基于 react 的项目基本都会用到此库

https://react-router.docschina.org/

基本使用:

  1. 明确界面中的导航区、展示区

  2. 导航区 a 标签改为 Link 标签

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

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

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

  4. <App /> 最外侧包裹一个 <BrowserRouter /><HashRouter />

{/* 原生html中,靠<a>跳转不同的页面 */}
{/* <a className="list-group-item active" href="./about.html">
  About
</a>
<a className="list-group-item" href="./home.html">
  Home
</a> */}

{/* 在React中靠路由链接实现切换组件-编写路由链接 */}
<Link className="list-group-item" to="/about">
  About
</Link>
<Link className="list-group-item" to="/home">
  Home
</Link>

{/* 注册路由 */}
<Route path="/about" component={About} />
<Route path="/home" component={Home} />

路由组件与一般组件区别:

  1. 写法不同:

    一般组件:<Demo />

    路由组件:<Route path="/demo" component={Demo} />

  2. 存放位置不同:

    一般组件:components

    路由组件:pages

  3. 接收到的 props 不同:

    一般组件:写组件标签时传递了什么,就能收到什么

    路由组件:接收到三个固定属性 historylocationmatch

    history:
        go: ƒ go(n)
        goBack: ƒ goBack()
        goForward: ƒ goForward()
        push: ƒ push(path, state)
        replace: ƒ replace(path, state)
    location:
      pathname: "/about"
      search: ""
      state: undefined
    match:
      params: {}
      path: "/about"
      url: "/about"
    

NavLink 基本使用

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

{...this.props} 不仅可以把在内联标签属性添加过来,还能把标签体的内容(children)拿过来填写在标签体内部

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

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

// 组件调用
<MyNavLink to="/about">About</MyNavLink>

Switch 基本使用

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

嵌套路由(匹配 重定向)

模糊匹配

  • to="/home/a/b" 可以匹配上 path="/home"
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home/a/b">Home</MyNavLink>
<Switch>
    <Route path="/about" component={About} />
    <Route path="/home" component={Home} />
</Switch>
  1. 默认使用的是模糊匹配(【输入的路径】必要要包含【匹配的路径】,且顺序要一致)
  2. 开启严格匹配 <Route exact path="/about" component={About} />
  3. 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

重定向

  • 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到 Redirect 指定的路由
<Switch>
  <Route path="/about" component={About} />
  <Route path="/home" component={Home} />
  <Redirect to="/about" />
</Switch>

嵌套路由

  1. 注册子路由时要写上父路由的 path 值
  2. 路由的匹配时按照注册路由的顺序进行的
<MyNavLink to="/home/news">News</MyNavLink>
<MyNavLink to="/home/message">Message</MyNavLink>

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

向路由组件传递参数

state = {
  messageArr: [
    { id: '01', title: '消息1' },
    { id: '02', title: '消息2' },
    { id: '03', title: '消息3' },
  ],
}

<ul>
  {messageArr.map(msgObj => {
    return (
      <li key={msgObj.id}>
        <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>
      </li>
    )
  })}
</ul>
  1. params 参数

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

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

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

{/* 向路由组件传递params参数 */}
<Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>

{/* 声明接收params参数 */}
<Route path="/home/message/detail/:id/:title" component={Detail} />

// 接收params参数
const { id, title } = this.props.match.params
  1. search 参数

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

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

    接收参数:const {search} = this.props.location; const {name, age} = qs.parse(search.slice(1))

    备注:获取到的 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} />

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

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

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

    接收参数:const {name, age} = 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 || {}

编程式路由导航

默认是 push 模式

<MyNavLink to="/home/news">News</MyNavLink>
<MyNavLink replace to="/home/message">Message</MyNavLink>

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

  • this.props.history.push()
  • this.props.history.replace()
  • this.props.history.gpBack()
  • this.props.history.goForward()
  • this.props.history.go()
import React, { Component } from 'react'
import { Link, Route } from 'react-router-dom'
import Detail from './Detail'

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 })
  }

  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>

                {/* 向路由组件传递search参数 */}
                {/* <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link> */}

                {/* 向路由组件传递state参数 */}
                {/* <Link to={{ pathname: '/home/message/detail', state: { id: msgObj.id, title: msgObj.title } }}>{msgObj.title}</Link> */}

                <button
                  onClick={() => {
                    this.pushShow(msgObj.id, msgObj.title)
                  }}
                >
                  push操作
                </button>
                <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} />
      </div>
    )
  }
}

withRouter

  • withRouter 可以加工一般组件,让一般组件具备路由组件所特有的API
  • withRouter 的返回值是一个新组件
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'

class Header extends Component { }

export default withRouter(Header)

BrowerRouter 与 HashRouter 的区别

  1. 底层原理不一样

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

    HashRouter 使用的是 URL 哈希值

  2. path 表现形式不一样

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

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

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

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

    HashRouter 刷新后会导致路由 state 参数丢失

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值