ReactRouter,让你滴应用能跳来跳去

上集精彩

回顾上集:

上集俺讲了个案例。

本集继续

😁😁😁,先别嫌俺烦,俺先叨叨(确实烦😅)

ReactRouter

使用React这些工具所编写的项目通常都是单页应用(SPA),上述的案例都可以叫单页应用。单页应用中,整个应用中只含有一个页面,React会根据不同的状态在应用中显示出不同的组件。要想实现多页面应用,需要引入一个新的工具React Router,React Router为我们提供一种被称为客户端路由的东西,通过客户端路由可以将URL地址和React组件进行映射,当URL地址发生变化时,它会根据设置自动的切换到指定组件。并且这种切换完全不依赖于服务器。换句话说,在用户看来浏览器的地址栏确实发生了变化,但是这一变化并不由服务器处理,而是通过客户端路由进行切换。

官网:Home v6.21.3 | React Router

React Router6新出来的,虽然与5有很大的区别和新特性,5和6都认识下吧,相当于一个过度吧。

css 复制代码
npm install react-router-dom@5

index.js 中引入 BrowserRouter 并包裹App组件(或起个别名 BrowserRouter as Router

然后就可以在项目中使用React Router了。

Route 组件

  • React Router 的url地址可以和组件进行映射(用户访问某个地址时,与其对应的组件会自动挂载)

不知道看到这你会不会这样想,不是说用户访问某个地址时,与其对应的组件会自动挂载,那俺访问了 /about 后,/ 路径的Home组件还会显示呢。

当 Route 的路径被访问,其对应组件就会对应挂载,其实这句话没毛病,你想的那是叫严格匹配。注意哈,默认情况下 Route 不能严格匹配,只要url地址地址的头部与path一致,组件就会挂载。要想做到你想的那样的严格匹配的话,添加个 exact

js 复制代码
{ /* exact 路径是否严格匹配 */ }
<Route exact path="/" component={Home} />
  1. component 用来指定路由匹配后被挂载的组件,需要直接传递组件的类,通过component构建的组件,它会自动创建组件并传递参数(直接在组件内传递props即可看到)
js 复制代码
// components/About/index.js
const About = (props) => {
  console.log(props);
  • match -> 匹配的信息

    • isExact 检查路径是否完全匹配

    • params 请求的参数

      About组件的path修改下 path="/about/:id" ,然后控制台打印 console.log(props.match.params) :

      改进下:

  • location -> 地址信息

  • history -> 控制页面的跳转

    • push() 跳转页面(可回退到之前页)
    • replace() 替换页面(不可回退到之前页)
  1. 除了 component 挂载组件,还有另一个方式 render

render 也可以用来指定被挂载的组件,它需要一个回调函数作为参数,回调函数的返回值会最终被挂载,它不会向 component 一样自动传递三个属性,要想得到这三个属性需要这样:

组件通过props就可接收到这三个属性

  1. 除了 render 挂载组件,还有另一个方式 children

children 也可以用来指定被挂载的组件,用法有两种:

  • 和render类似,传递回调函数(直接把上图的render改为children就行),当children设置一个回调函数时,该组件无论路径是否匹配都会挂载
  • 可以直接传递组件 <Route exact path="/about/:id" children={<About />} /> (或者这样写 <Route exact path="/about/:id"><About /></Route>

除了通过props获取三个对象外,还可以通过钩子函数来获取:

路由的嵌套

同样也可以将嵌套路由放在About组件里,只是层级不一样了,使用还是不变的。

那如果about的嵌套路由有很多的话,想把 /about/xxx/xxx~~~ 中的about动态获取(即父组件地址)

Prompt组件

可以将路由Route统一放在一个Switch中,一个Switch中只会有一个路由显示

当你使用到表单时,如果你没有写完表单内容跳转到其他页面了,再返回的话,表单的内容就会重置,这样会很麻烦,使用Prompt组件,会在你离开页面之前弹窗提示

改进下:(就是防止误操作导致数据丢失)

Redirect组件 重定向

在About组件中使用Redirect组件重定向到form中

js 复制代码
// components/About/index.js
import { Route, useRouteMatch, Redirect } from "react-router-dom";
​
{/* to 重定向到xxx
          from 从xxx 如 from={"/abc"} to={"/form"} 输入地址abc到form
           */}
        <Redirect from={"/abc"} to={"/form"} />

然后在App.js中导入并使用,当你点击两个链接时,它是不会向服务器重新发送请求的。但当你把 Link 换成 a 的话,那就不行了,a 会重新向服务器发送请求,不能这样干。

  • NavLink 的功能会比 Link 更加丰富:它主要会涉及到样式

看,不管你咋跳转,它都不会发请求,当你跳转到对应页面时,它就会显示对应路径的链接粉色加下划线。

两种 Router

  • HashRouter 会通过url地址中的hash值来对地址进行匹配(就是根路径后多个 # 号)

  • BrowserRouter 直接通过url地址进行组件的跳转,(不带 # 号)

    当通过点击NavLink构建的链接进行跳转时,跳转并没经过服务器,这没问题

    但当刷新页面时或通过普通链接跳转时,会向服务器发送请求加载数据,这时的请求并没有经过react router,所以返回404

  • 解决方案:

    • 使用HashRouter 重新构建并粘贴。服务器是不会处理带hash值的地址,所以使用HashRouter后请求将有react route处理

    • 修改服务器的配置,将所有请求都转发到index.html。找到nginx下的conf目录,打开nginx.conf,找到对应字段并进行如下修改: try_files $uri /index.html; 意思就是不管你访问啥url地址,最后都给俺转发到index.html里

      重新加载一下服务器 .\nginx.exe -s reload 。可能会打印错误,俺没用过nginx就用了比较死板的法子,打开任务管理器,停止nginx服务。然后再点击nginx.exe文件重启服务就行了。

nginx服务器:nginx news

下载后,解压,点击 nginx.exe 即可启动服务器,访问 127.0.0.1

项目打包后,将其目录下所有文件粘贴到nginx下的html目录里。

要想停止服务的话,在nginx安装目录下打开终端:

ReactRouter6

之前讲的ReactRouter都是5的知识点,ReactRouter6相对于5来讲改动还是很大的,把很多东西都化繁为简了,体积相对于之前的版本也小了很多比如 Switch,history,match等在6中都没有了,被其他东西替代了

cmd 复制代码
npm i react-router-dom
  • 可以通过useParams() 来获取参数:
  • useLocation() 还在:获取当前地址的信息
js 复制代码
const location = useLocation();
  console.log(location);
  • useMatch() 检查当前url是否匹配某个路由
js 复制代码
const match = useMatch("/about");
  console.log(match);

如果路由匹配,则返回个对象,不匹配返回null

  • useNavigate() 获取一个用于页面跳转的函数:
js 复制代码
const navigate = useNavigate();
  const clickHandler = () => {
    // navigate("/student/1"); // 使用push 会产生历史记录 可回退
    navigate("/student/1", { replace: true }); // 使用replace 不会产生历史记录 不可回退
  };

v6中的嵌套路由

  • 通过子路由来对Good进行映射 /about/good:
js 复制代码
// App.js
<Route path="about/*" element={<About />} />
​
// components/About/index.js
<Routes>
        <Route path="good" element={<Good />} />
      </Routes>

v6中是默认严格匹配的,在后面加上 ***** 后就可不按照严格匹配来了。

注意了: 你看这,App.js中有路由,About组件中又有路由,要是每个组件都设置路由的话,那是不是非常不便于管理和维护。想要的是所有的路由可以统一写在一个位置,你可以这样:

js 复制代码
// App.js
<Route path="about" element={<About />}>
          {/* 嵌套路由 */}
          <Route path="good" element={<Good />} />
        </Route>

嵌套路由也用了,但当你访问 /about/good 时并没有出现Good组件的页面,此时你还需要一步,在About组件中使用Outlet组件,它用来表示嵌套路由中的组件,当嵌套路由中的路径匹配成功,Outlet则表示嵌套路由中的组件,反之啥也不显示

js 复制代码
import { Outlet } from "react-router-dom";
​
// components/About/index.js
const About = () => {
  return (
    <>
      <h1>Welcome to the About Page!</h1>
      <ul>
        <li>React</li>
      </ul>
      <Outlet />
    </>
  );
};

Navigate 组件用来跳转到指定的位置,默认使用push跳转,回退不了,要想回退的话,加个 replace

js 复制代码
<Navigate to={"/student/3"} replace />

改了一种方式去设置样式:

js 复制代码
<NavLink
            style={({ isActive }) => {
              return isActive ? { color: "black" } : null;
            }}
            to="/student/2"
          >
            Student
          </NavLink>

ReactRouter 工程化

抽离路由模块

工程化即模块化,router示例写在index.js中的话就不符合了。能不能创建个单独的模块,将router相关的放到其中,可以抽离路由模块。

  • 在src下创建 pages : 用于放页面的;并在src下兴建 router ,专门用来放路由相关的

接着再导入到index.js中并使用

js 复制代码
import router from "./router";

路由导航

  1. 前面讲的Link、NavLink、useNavigate() 都可用于路由跳转,要说区别的话
  • 声明式导航:Link、NavLink (会被转义为a链接)
  • 编程式导航:useNavigate() 通过该钩子函数使用导航
  1. 通过 useSearchParams() 也可以实现路由传参

路由嵌套(子路由)

  • 通过 children 配置子路由:
  • 在Layout组件中添加二级路由出口:
  • 如何在显示Layout组件的同时,显示Home组件,默认二级路由就是将 path: "/home" 换成 index: true
  • 当路由找不到时,用404路由兜底
js 复制代码
{
    path: "*",
    element: <NotFound />
  }
  • 要想将路由模式换成hash模式直接将 createBrowserRouter 换为 createHashRouter

样式相关

补充: 好像之前也没有说到样式相关的内容,那就在这里补充一下吧。希望不会太晚😅

内联样式和样式表

  • 通过在标签内直接使用 style={{}}
  • 当内联样式多的话,可以把它提出来:
  • 动态设置样式:需要借助 useState

内联样式肯定不适合大项目。

  • 样式表的话:

它也不适合,有缺点,import "./index.css"; 这样如果是在App.js中引入那就是全局引入,如果再在 src/index.js 下引入的话那就会形成样式冲突(类名重复的话),不适合大范围的使用,这种方式适合引入一些通用样式。

CSS Module

模块化CSS,适合大范围的使用(一种自动写类名的方式)。

使用步骤:

  1. 创建css文件时,以 xxx.module.css 命名,说明你创建的时一个css模块
  2. 在组件中引入css,import classes from "./index.module.css";
  3. 通过 classes 来设置类,className={classes.title}。CSS模块可以动态的设置唯一的class值(类名都是自动生成的,就是确保唯一性)

Fragment

拓展: 在上图中,你可能会看到这个 <></> ,不知道你会不会对这玩意产生好奇,也不管你了,爱咋咋地,俺先简单说下这是个什么东西。

在react中,组件都必须有个跟容器,然后在跟标签里编写子标签。好,那俺在每个组件写个div父容器呗,但绝大多数都用不上这个div呀(就是个多鱼的结构)。但俺就是不想加那个div,俺就感觉难受。好,那俺有三种法子来解决:

  1. 方法一:自定义个组件:组件直接引入父容器Out并使用就行了
js 复制代码
// src/Out.js
const Out = (porps) => {
  // 只会返回子元素
  return porps.children;
};
​
export default Out;
  1. 方法二:使用 Fragment:它是由React提供的专门作为父容器的组件,它只会讲里面的子元素直接返回,不会创建任何多鱼的元素
js 复制代码
import { Fragment } from "react";
​
const Good = () => {
  return (
    <Fragment>
      <h1>Welcome to the Good Page!</h1>
    </Fragment>
  );
};
  1. 方法三:使用空标签 <></> ,它是react提供的语法糖

下集精彩

好了,到这,React的所有基础知识点(有点绝对了😀)。该说的都说了,能保证在后期的项目中够用就行了,不行的话再查,再学呗。正所谓:我不入地狱谁入地狱 ,哎!不对,应该是:活到老学到老。不好意思好😁

下篇俺会讲到哪些知识点呢: 俺将正式迈入工程化的大门。虽然本集已讲了点点了ReactRouter的工程化,算是半只脚进门吧。

  • React 工程化;

只要懂得这篇的知识点就很棒了。

相关推荐
M_emory_5 分钟前
解决 git clone 出现:Failed to connect to 127.0.0.1 port 1080: Connection refused 错误
前端·vue.js·git
Ciito8 分钟前
vue项目使用eslint+prettier管理项目格式化
前端·javascript·vue.js
成都被卷死的程序员41 分钟前
响应式网页设计--html
前端·html
fighting ~44 分钟前
react17安装html-react-parser运行报错记录
javascript·react.js·html
老码沉思录1 小时前
React Native 全栈开发实战班 - 列表与滚动视图
javascript·react native·react.js
abments1 小时前
JavaScript逆向爬虫教程-------基础篇之常用的编码与加密介绍(python和js实现)
javascript·爬虫·python
mon_star°1 小时前
将答题成绩排行榜数据通过前端生成excel的方式实现导出下载功能
前端·excel
Zrf21913184551 小时前
前端笔试中oj算法题的解法模版
前端·readline·oj算法
老码沉思录1 小时前
React Native 全栈开发实战班 - 状态管理入门(Context API)
javascript·react native·react.js
文军的烹饪实验室2 小时前
ValueError: Circular reference detected
开发语言·前端·javascript