五、React路由

郁子大约 6 分钟约 1804 字笔记React16.8尚硅谷张天禹React Router

(一)相关概念的理解

1.SPA

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

2.路由

1)什么是路由?

  • 一个路由就是一个映射关系(key:value
  • key 为路径, value 可能是 functioncomponent

2)路由分类

  • 后端路由(以 Node Express 为例)
    • valuefunction,用来处理客户端提交的请求
    • 注册路由:route.get(path, function(req, res))
    • 工作过程:当 node 接收到一个请求时,根据请求路径找到匹配的路由,调用路由中的函数来处理请求,返回响应数据
/*
  请求地址: http://localhost:3000/search/users?q=aa
  后台路由
  key: /search/users
  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);
  });
});
  • 前端路由
    • 底层逻辑是操作浏览器 BOM 对象的 history 属性 window.history
    • 浏览器端路由: valuecomponent ,用于展现页面内容
    • 注册路由:<Route path="/test" component={Test} />
    • 工作过程:当浏览器的 path 变为 /test 时,当前路由组件就会变为 Test 组件

3)react-router-dom

  • 文档:https://react-router.docschina.org/open in new window
  • react-router 库有三种实现
    • web:react-router-dom,供 web 开发人员使用
    • native:供 React Native 开发人员使用
    • anywhere:web 和 native 都可以使用
  • react 的一个插件库
  • 专门用来实现一个 SPA 应用
  • 基于 React 的项目基本都会用到此库

3.组件分类

1)一般组件

  • 程序员自定义的组件
  • 通过 <Demo /> 引入使用
  • 一般放在 components 目录下
  • 写组件标签时传递了什么,props 就收到什么

2)路由组件

  • 路由器匹配的组件
  • 通过 <Link to="/demo" component={Demo} /> 引入使用
  • 一般放在 pages 目录下
  • props 固定收到三个属性

history:

  1. go: ƒ go(n)
  2. goBack: ƒ goBack()
  3. goForward: ƒ goForward()
  4. push: ƒ push(path, state)
  5. replace: ƒ replace(path, state)

location:

  1. pathname: "/about"
  2. search: ""
  3. state: undefined

match:

  1. params: {}
  2. path: "/about"
  3. url: "/about"

(二)基本路由使用

1.准备

1)下载 react-router-dom

npm i react-router-dom@5

2)引入 bootstrap.css

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

2.界面分区

  • 导航区:渲染路由菜单的区域
  • 展示区:渲染当前路由匹配的组件

3.导航区 —— 路由链接

  • 将 a 标签改为 Link 标签:<Link to="/xxx">Demo</Link>
import { Link } from "react-router-dom";

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

4.展示区 —— 注册路由

  • 进行路径匹配:<Route path="/xxx" component={Demo} />
import { Route } from "react-router-dom";

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

5.App 组件最外侧包裹路由器组件

  • BrowserRouter:对应 history.createBrowserHistory
  • localhost:3000/about
import { BrowserRouter } from "react-router-dom";

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root"),
);
  • HashRouter:对应 history.createHashHistory
    • localhost:3000/#/about
    • # 后为哈希值,不作为资源传给服务器(localhost:3000)
import { HashRouter } from "react-router-dom";

ReactDOM.render(
  <HashRouter>
    <App />
  </HashRouter>,
  document.getElementById("root"),
);

(三)BrowserRouterHashRouter 的区别

1.底层原理不同

  • BR 使用的是 H5 的 history API,不兼容 IE9 及以下版本
  • HR 使用的是 URL 的哈希值

2.path 表现形式不同

  • BR 路径中没有"#"
    • 如:localhost:3000/demo/test
  • HR 路径中有"#"
    • 如:localhost:3000/#/demo/test

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

  • BR 没有任何影响,因为 state 保存在 history 对象中
  • HR 刷新后会导致路由 state 参数丢失

4.备注

  • HR 可以用于解决一些路径错误相关的问题
  • NavLink 可以实现路由链接的高亮
  • 通过 activeClassName 指定样式名
<NavLink activeClassName="atguigu" className="list-group-item" to="/about">
  About
</NavLink>
  • 标签体内容是一个特殊的标签属性:children
  • 通过 this.props.children 可以获取标签体内容
// components/MyNavLink/index.jsx
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="atguigu"
      //   className="list-group-item"
      //   {...this.props}
      // >
      //   {this.props.children}
      // </NavLink>
      <NavLink activeClassName="atguigu" className="list-group-item" {...this.props} />
    );
  }
}

// App.jsx
<MyNavLink to="/about">About</MyNavLink>;

(五)Switch 的使用

  • 通常情况下,pathcomponent 是一一对应的关系
  • Switch 可以提高路由匹配效率(单一匹配)
// Switch仅匹配第一个符合条件的路由路径
<Switch>
  <Route path="/about" component={About} />
  <Route path="/home" component={Home} />
  <Route path="/home" component={TestSwitch} />
</Switch>

(六)解决多级路径刷新页面样式丢失的问题

1.public/index.html 中引入样式不写 .//

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

2.public/index.html 中引入样式不写 ./%PUBLIC_URL%

  • 常用,只适用于脚手架
<link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.css" />

3.路由包裹不使用 BrowserRouter 使用 HashRouter

  • 不推荐

(七)路由的严格匹配与模糊匹配

1.默认使用的是模糊匹配

  • 简单记:【输入的路径】必须包含【要匹配的路径】,且顺序要一致
{/* 编写路由链接——在React中靠路由链接实现切换组件 */}
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink>
<MyNavLink to="/home/a/b">Home</MyNavLink>
<MyNavLink to="/a/home/b">Home</MyNavLink>

{/* 注册路由 */}
{/* Switch仅匹配第一个符合条件的路由路径 */}
<Switch>
  <Route exact path="/about" component={About} />
  <Route exact path="/home" component={Home} />
</Switch>

2.开启严格匹配

  • 严格匹配不要随便开启,需要再开,有时候开启会导致无法继续匹配二级路由
<Route exact={true} path="/about" component={About} />
<Route exact path="/about" component={About} />

(八)Redirect 的使用

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

(九)嵌套路由

  • 注册子路由时要写上父路由的 path
  • 路由的匹配是按照注册路由的顺序进行的
<Switch>
  <Route path="/home/news" component={News} />
  <Route path="/home/message" component={Message} />
  <Redirect to="/home/news" />
</Switch>

(十)向路由组件传递参数

1. params 参数

1)路由链接

  • 携带参数
<Link to="/demo/test/tom/18">详情</Link>

2)注册路由

  • 声明接收
<Route path="/demo/test/:name/:age" component={Test} />

3)Test 组件接收参数

const { name, age } = this.props.match.params;

2. search 参数

1)路由链接

  • 携带参数
<Link to="/demo/test/?name=tom&age=18">详情</Link>

2)注册路由

  • 无需声明接收,正常注册即可
<Route path="/demo/test" component={Test} />

3)Test 组件接收参数

  • 获取到的 searchurlencoded 编码字符串
  • 需要借助 query-string 库解析
import qs from "query-string";
const { search } = this.props.location; // ?name=tom&age=18
const { name, age } = qs.parse(search.slice(1));

3. state 参数

1)路由链接

  • 携带参数
<Link
  to={{
    pathname: "/demo/test",
    state: {
      name: "tom",
      age: 18,
    },
  }}
>
  详情
</Link>

2)注册路由

  • 无需声明接收,正常注册即可
<Route path="/demo/test" component={Test} />

3)Test 组件接收参数

  • 当前标签页刷新可以保留住参数
  • 因为 BrowserRouter 一直在维护 history 属性
  • 清空本地缓存后参数丢失
const { name, age } = this.props.location.state || {};

(十一)编程式路由导航

  • 借助 this.props.history 对象上的 API 对操作路由跳转、前进、后退
    • this.props.history.push()
    • this.props.history.replace()
    • this.props.history.goBack()
    • this.props.history.goForward()
    • this.props.history.go()
上次编辑于: