07_React路由

目录

一、什么是SPA?

二、什么是路由? 

后端路由

前端路由

1. 浏览器的历史记录是一个栈的结构

2. BOM与DOM

三、react-router-dom

3.1 基本使用

3.1.1 一般组件与路由组件

3.1.3 Switch组件

3.1.4 可能出现样式丢失问题

3.1.5 路由的模糊匹配与精准匹配

3.1.6 Redirect重定向

3.2 嵌套路由

3.3 向路由参数传递参数

第一种 传递params参数

第二种 传递search参数

第三种 传递state参数

3.4 路由跳转的两种模式(push与replace)

3.5 编程式路由

3.6  如何在一般组件中使用路由组件的API?(WithRouter)

3.7 BrowserRouter与HashRouter的区别

1. 底层原理不同

2. path表现形式不同

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


一、什么是SPA?

  • 单页面应用
  • 整个应用只有一个完整的页面
  • 点击页面中的链接不会刷新整个页面,只会做页面的局部更新
  • 数据都需要通过ajax请求获取, 并在前端异步展现

二、什么是路由? 

一个路由就是一个映射关系(key:value)

key为路径, value可能是function或component

  • 后端路由

    1. 工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据
    2. 注册路由: router.get(path, function(req, res))
    3. 理解: value是function, 用来处理客户端提交的请求。
app.get("/search/users", function (req, res) {
  const {q} = req.query
  axios({
    url: 'https://api.github.com/search/users',
    params: {q}
  }).then(response => {
    res.json(response.data)
  })
})
  • 前端路由

    1. 浏览器端路由,value是component,用于展示页面内容。
    2. 注册路由: <Route path="/test" component={Test}>
    3. 工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件
    4. 前端路由借助BOM上的history

补充:

1. 浏览器的历史记录是一个栈的结构

栈和队列就是常见的受限的线性结构。其限制是仅允许在表的一端进行插入和删除操作。相当于堆积木,最下方元素是栈底,最上方是栈顶。

向一个栈插入新元素又称为进栈、入栈或压栈,它是把新元素放到栈顶元素的上方,使之成为栈顶。从一个栈删除元素又称作出栈或退栈,它把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>前端路由的基石_history</title>
</head>
<body>
	<a href="http://www.atguigu.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>
</body>
</html>

每次push,都往上堆,最上方形成栈顶,history.html为栈底。replace则为替代,也就是替代当前栈顶为 /test3 

 

 锚点跳转不会引起页面的刷新,锚点跳转可以留下历史记录

2. BOM与DOM

javascript 由三部分组成,ECMAScript和Web API(DOM和BOM)

1. DOM:文档对象模型,用于处理HTML和XML文档内容的标准编程接口API,一般用于修改页面内容

  • 页面由有层次的树状结构节点构成,简称 DOM 树。
  • 浏览器根据 HTML 标签生成的 JS 对象,简称 DOM 对象。JS通过document对象 来对HTML/XML文档进行操作。

2. BOM:浏览器对象模型,用于操作浏览器而出现的API,一般用于调整浏览器窗口大小、标签页跳转

  • BOM对象最根本的是window,是JS中的全局对象
  • window 对象下包含了 navigator、location、document、history、screen 5个属性

 以及

  • setTimeout、setInterval
  • alert()
  • parseInt()

三、react-router-dom

3.1 基本使用

  • 下载库  
npm i react-router-dom@5
  • 引入bootstrap
 <link
      rel="stylesheet"
      href="%PUBLIC_URL%/css/bootstrap.css"
    />
  • App.js
import "./App.css"
import { Link, Route } from "react-router-dom"
import About from "./Component/About"
import Home from "./Component/Home"

function App() {
  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">
            {/* <a></a>跳页面 */}
            {/* <a
              className="list-group-item active"
              href="./about.html"
            >
              About
            </a>
            <a
              className="list-group-item"
              href="./home.html"
            >
              Home
            </a> */}
            {/* 路由link切组件,to一般小写*/}

            <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注册路由 */}
              <Route
                path="/about"
                component={About}
              />
              <Route
                path="/home"
                component={Home}
              />
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}

export default App
  •  About组件
import React, { Component } from "react"

export default class About extends Component {
  render() {
    return <h3>我是About的内容</h3>
  }
}

常见错误

1. Invariant failed: You should not use <Link> outside a <Router>

最外层必须用 BrowserRouter 或 HashRouter 标签包裹起来

一般在index.js里设置

3.1.1 一般组件与路由组件

一般组件存放在componnets,props信息需要自行编辑

  <Header a={1} />

 路由组件存放在pages,会收到路由器给传递的三个最重要的props信息

 <Route path="/about" component={About} />

NavLink可以实现路由链接的高亮,通过 activeClassName 指定样式名

标签体内容是个特殊的标签属性 children ,this.props.children可以获取标签图内容

  • index.html
.demo {
        background-color: rgb(45, 146, 45) !important;
        color: white !important;
      }
  • 封装NavLink
import React, { Component } from "react"
import { NavLink } from "react-router-dom"
export default class MyNavLink extends Component {
  render() {
    //标签属性,标签体内容 children
    console.log(this.props)
    return (
      <NavLink
        activeClassName="demo"
        className="list-group-item"
        {...this.props}
      />
    )
  }
}
  • App.js
            {/* MyNavLink标签属性,to,a */}
            <MyNavLink
              to="/about"
              a={1}
            >
              {/* 标签体内容,也是特殊的标签属性 children */}
              About
            </MyNavLink>
            {/* <NavLink
              to="/home"
              children="Home"
            /> */}
            <MyNavLink to="/home">Home</MyNavLink>

3.1.3 Switch组件

注册多个路由时,用switch组件包裹,匹配上之后不会继续匹配

通常情况下,path和component是一一对应,Switch可以提高匹配效率(单一匹配)

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

3.1.4 可能出现样式丢失问题

            <MyNavLink
              to="/news/about"
              a={1}
            >
              About
            </MyNavLink>
            <MyNavLink to="/news/home">Home</MyNavLink>
              <Switch>
                <Route
                  path="/news/about"
                  component={About}
                />
                <Route
                  path="/news/home"
                  component={Home}
                />
              </Switch>

解决方案一

 index.html

写入样式时 / , 不写 ./ 

 <link
      rel="stylesheet"
      href="/css/bootstrap.css"
    />

 或 %PUBLIC_URL%

 <link
      rel="stylesheet"
      href="%PUBLIC_URL%/css/bootstrap.css"
    />

解决方案二

index.js, 使用 HashRouter

import { HashRouter } from "react-router-dom"

3.1.5 路由的模糊匹配与精准匹配

默认模糊匹配,to,path从左开始匹配,path一定要有to中按顺序的路径

exact={true}精准匹配

             <MyNavLink
              to="/about"
              a={1}
            >
              About
            </MyNavLink>
            <MyNavLink to="/home/a/bc">Home</MyNavLink>
              <Switch>
                <Route
                  exact={true}
                  path="/about"
                  component={About}
                />
                <Route
                  path="/home/a"
                  component={Home}
                />
              </Switch>

3.1.6 Redirect重定向

一般写在注册路由的最下方,当所有路由无法匹配时,跳转到指定路由

        <MyNavLink
              to="/about"
              a={1}
            >
              About
            </MyNavLink>
            <MyNavLink to="/home">Home</MyNavLink>
          </div>
               <Switch>
                <Route
                  exact={true}
                  path="/about"
                  component={About}
                />
                <Route
                  path="/home"
                  component={Home}
                />
                {/* 重定向 */}
                <Redirect to="/home" />
              </Switch>

3.2 嵌套路由

路由的注册的是有顺序的,App.js是最先开始注册的。注册子路由时要写上父路由的path,路由的匹配是按照注册路由的顺序开始的。

  • Home/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>
    )
  }
}
  • Home/Messages组件
import React, { Component } from "react"

export default class Message extends Component {
  render() {
    return (
      <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>
    )
  }
}
  • Home组件
import React, { Component } from "react"
import MyNavLink from "../../Component/MyNavLink"
import { Switch, Route, Redirect } from "react-router-dom"
import News from "./News"
import Message from "./Message"
export default class Home extends Component {
  render() {
    return (
      <div>
        <h2>Home组件内容</h2>
        <div>
          <ul className="nav nav-tabs">
            <li>
              <MyNavLink to="/home/news">News</MyNavLink>
            </li>
            <li>
              <MyNavLink to="/home/message">Message</MyNavLink>
            </li>
          </ul>
          <div>
            <Switch>
              <Route
                path="/home/news"
                component={News}
              />
              <Route
                path="/home/message"
                component={Message}
              />
              <Redirect to="/home/news" />
            </Switch>
          </div>
        </div>
      </div>
    )
  }
}

3.3 向路由参数传递参数

第一种 传递params参数

  • Home/Message组件
import React, { Component } from "react"
import { Link, Route } from "react-router-dom"
import Detail from "./Detail"
export default class Message extends Component {
  state = {
    Msg: [
      {
        id: "01",
        title: "message1",
        content: "消息1",
      },
      {
        id: "02",
        title: "message2",
        content: "消息2",
      },
      {
        id: "03",
        title: "message3",
        content: "消息3",
      },
    ],
  }
  render() {
    const { Msg } = this.state
    return (
      <div>
        <ul>
          {Msg.map((msgobj) => {
            return (
              <li key={msgobj.id}>
                {/* 向路由组件传递params参数 */}
                <Link
                  to={`/home/message/detail/${msgobj.id}/${msgobj.title}`}
                >
                  {msgobj.title}
                </Link>
                &nbsp;&nbsp;
              </li>
            )
          })}
        </ul>
        <hr />

        {/* 声明接收params参数 */}
        <Route
          path="/home/message/detail/:id/:title"
          component={Detail}
        />
      </div>
    )
  }
}
  • Home/Message/Detail组件
import React, { Component } from "react"
const data = [
  {
    id: "01",
    content: "消息详情1",
  },
  {
    id: "02",
    content: "消息详情2",
  },
  {
    id: "03",
    content: "消息详情3",
  },
]
export default class Detail extends Component {
  render() {
    //console.log(this.props)
    //接收params参数
    const { id, title } = this.props.match.params
    const findById = data.find((detailObj) => {
      return detailObj.id === id
    })
    return (
      <ul>
        <li>ID:{id}</li>
        <li>Title:{title}</li>
        <li>Content:{findById.content}</li>
      </ul>
    )
  }
}

要点:

路由链接携带参数:

<Link to={`/home/message/detail/${msgobj.id}/${msgobj.title}`}>
{msgobj.title}
</Link>

注册路由声明接收:

<Route path="/home/message/detail/:id/:title" component={Detail}/>

接收参数:

  const { id, title } = this.props.match.params

第二种 传递search参数

import qs from "qs"

let obj = { name: "Tom", age: 18 }
console.log(qs.stringify(obj)) //name=Tom&age=18

let str = "name=Mike&age=16"
console.log(qs.parse(str))//{name: "Mike", age: "16"}
  •  向路由组件传递search参数
<Link
to={`/home/message/detail/?id=${msgobj.id}&title=${msgobj.title}`}
>
{msgobj.title}
</Link>
  •  search无需声明接收,正常接收即可
<Route
path="/home/message/detail"
component={Detail}
/>
  • 接收search参数,是urllencoded编码字符串,需要借助qs解析
//key=value&key=value :urlencoded编码
const { search } = this.props.location
const { id, title } = qs.parse(search.slice(1))

第三种 传递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

报错内容

1. Uncaught TypeError: Cannot destructure property 'id' of 'this.props.location.state' as it is undefined.

const { id, title } = this.props.location.state || {}

2.Uncaught TypeError: Cannot read property 'content' of undefined

const findById = data.find((detailObj) => {
      return detailObj.id === id
}) || {}

总结

1. location中state默认是undefined

2. state传递参数路径无提示

3. 三种传递参数方式,刷新参数都不会丢失

3.4 路由跳转的两种模式(pushreplace

默认路由跳转是push形式,会留下痕迹,进行压栈操作

replace是替换,替换栈顶,不留下痕迹

<Link
replace={true}
to={{
pathname: "/home/message/detail",
state: { id: msgobj.id, title: msgobj.title },
}}
>
{msgobj.title}
</Link>

3.5 编程式路由

只有路由组件才有history,主要就是history上的API,push、replace、goBack、goForWard、go等

import React, { Component } from "react"
import { Link, Route } from "react-router-dom"
import Detail from "./Detail"
export default class Message extends Component {
  state = {
    Msg: [
      {
        id: "01",
        title: "message1",
        content: "消息1",
      },
      {
        id: "02",
        title: "message2",
        content: "消息2",
      },
      {
        id: "03",
        title: "message3",
        content: "消息3",
      },
    ],
  }
  //push跳转
  pushShow = (id, title) => {
    //编程式路由跳转
    //this.props.history.push(`/home/message/detail/${id}/${title}`)
    //this.props.history.push(`/home/message/detail/?id=${id}&title=${title}`)
    this.props.history.push(`/home/message/detail`, { id, title })
  }
  //replace跳转
  replaceShow = (id, title) => {
    //replace跳转
    //this.props.history.replace(`/home/message/detail/${id}/${title}`)
    //this.props.history.replace(`/home/message/detail/?id=${id}&title=${title}`)
    this.props.history.replace(`/home/message/detail`, { id, title })
  }
  //回退
  backShow = () => {
    this.props.history.goBack()
  }
  //前进
  forwardShow = () => {
    this.props.history.goForward()
  }
  //go
  goShow = () => {
    //2:前进2步;-2:回退2步
    this.props.history.go(-2)
  }
  render() {
    const { Msg } = this.state
    return (
      <div>
        <ul>
          {Msg.map((msgobj) => {
            return (
              <li key={msgobj.id}>
                {/* 1.向路由组件传递params参数 */}
                {/* <Link to={`/home/message/detail/${msgobj.id}/${msgobj.title}`}>
                  {msgobj.title}
                </Link> */}
                {/* 2.向路由组件传递search参数 */}
                {/* <Link
                  to={`/home/message/detail/?id=${msgobj.id}&title=${msgobj.title}`}
                >
                  {msgobj.title}
                </Link> */}
                {/* 3.向路由组件传递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>
                {/* 路由的两种模式 push和replace */}
                {/* <Link
                  replace={true}
                  to={{
                    pathname: "/home/message/detail",
                    state: { id: msgobj.id, title: msgobj.title },
                  }}
                >
                  {msgobj.title}
                </Link> */}
                &nbsp;&nbsp;
              </li>
            )
          })}
        </ul>
        <hr />
        {/* 2.声明接收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.backShow}>回退</button>&nbsp;
        <button onClick={this.forwardShow}>前进</button>&nbsp;
        <button onClick={this.goShow}>go</button>
      </div>
    )
  }
}

3.6  如何在一般组件中使用路由组件的API?(WithRouter)

  • WithRouter可以加工一般组件,让一般组件具备 路由组件特有的API
  • WithRouter返回的是一个新的组件

  • Header组件
import React, { Component } from "react"
import { withRouter } from "react-router-dom"
class Header extends Component {
  //回退
  backShow = () => {
    this.props.history.goBack()
  }
  //前进
  forwardShow = () => {
    this.props.history.goForward()
  }
  //go
  goShow = () => {
    //2:前进2步;-2:回退2步
    this.props.history.go(-2)
  }
  render() {
    return (
      <div className="page-header">
        <h2>React Router Demo</h2>
        <button onClick={this.backShow}>回退</button>&nbsp;
        <button onClick={this.forwardShow}>前进</button>&nbsp;
        <button onClick={this.goShow}>go</button>
      </div>
    )
  }
}
//withRouter加工后Header会有路由组件的API
export default withRouter(Header)

3.7 BrowserRouter与HashRouter的区别

1. 底层原理不同

  • BrowserRouter:使用的是h5的history的API,不兼容IE9以下版本
  • HashRouter:使用的是URL的哈希值,可以解决路径错误问题

2. path表现形式不同

  • BrowserRouter无#,localhost:3000/demo/test
  • HashRouter路径包含#,localhost:3000/#/demo/test。#后的不会发送给服务器,不认为是一种请求资源的路径

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

  • BrowserRouter无任何影响,因为history保存在state中
  • HashRouter刷新后会导致路由state参数的丢失!!!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

才不吃胡萝卜嘞

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

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

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

打赏作者

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

抵扣说明:

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

余额充值