React基础知识三 router路由全指南

news2024/12/26 22:23:15

现在最新版本是Router6和Router5有比较大的变化,Router5和Router4变化不大,本文以Router6的写法为主,也会对比和Router5的不同。比较全面。

安装路由

npm i react-router-dom

基本使用

有两种Router,BrowserRouter和HashRouter,选哪个都可以,这里以HashRouter为例子,BrowserRouter用法上没什么区别。

先给App组件套上一层Router,这样就可以使用路由功能了。

import { BrowserRouter, HashRouter } from "react-router-dom";

root.render(
  <React.StrictMode>
  <HashRouter>
    <App />
  </HashRouter>
 </React.StrictMode>
);
或者
root.render(
  <React.StrictMode>
  <BrowserRouter>
    <App />
  </BrowserRouter>
 </React.StrictMode>
);

在App里面我们需要定义一个Routes组件,以及他的子组件Route,Route会将path映射成对应的组件。也就是动态加载我们的Home或者About组件。

import { Routes, Route } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";

function App() {
  return (
    <div className="App">
      <div>Header</div>
      <div>
        <Routes>
          <Route path="/home" element={<Home />} />
          <Route path="/about" element={<About />} />
        </Routes>
      </div>
      <div>Footer</div>
    </div>
  );
}

export default App;

Home和About就展示文本

import React from "react";
class Home extends React.PureComponent {
  render() {
    return <div>Home Page</div>;
  }
}
export default Home;

在浏览器中输入路径,注意我们使用的是HashRouter,所以路径前面需要加#号。

http://localhost:3001/#/home

效果就是Home组件的内容确实被动态加载了。
在这里插入图片描述

使用Link组件实现点击切换

上面的代码可以使用浏览器输入路径动态的切换内容。但一般用户不会直接在浏览器输入地址。一般我们会给个导航栏让用户点击,这个功能用Link就可以很容易实现。

Link内容直接写显示的文本,以及一个to属性用于指定路径,和Route的path属性对应上就可以了。

import { Routes, Route, Link } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";

function App() {
  return (
    <div className="App">
      <div>
        <Link to="/home">首页</Link>
        <Link to="/about">关于</Link>
      </div>
      <div>
        <Routes>
          <Route path="/home" element={<Home />} />
          <Route path="/about" element={<About />} />
        </Routes>
      </div>
      <div>Footer</div>
    </div>
  );
}

export default App;

这样,就可以通过Link组件实现点击动态切换路由内容了。
在这里插入图片描述

404页面

当所有的路径都匹配不到的时候,就匹配到了*。

<Route path="/home" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/login" element={<Login />} />
<Route path="*" element={<NotFound />} />

Link的一些额外属性

  • replace 是否重定向,默认false
  • reloadDocument 是否出现加载文档,会导致填的数据丢失,默认false
  • state 可以传值,在目标组件使用useLocation可以获取到值。
<Link to="/home" replace={false} reloadDocument={false} state={{name:"Tom"}}>

NavLink的使用(了解)

NavLink可以使链接样式有一些变化。但实际上我们是很少使用的,因为我们一般都自定义。作为了解就可以了。
当我们把Home组件的Link替换成NavLink的时候,当Home组件的NavLink被点击的时候,我们查看源代码,发现,a元素上面多了一个active的class。但NavLink被点击的时候,我们可以通过这个active class来做一些样式上的变化。
在这里插入图片描述
我们直接定义一个class,在组件里面引入,在点击的时候就会触发这个效果了。

.active {
  color: red;
  font-size: 20px;
}

支持下面的写法,函数参数是arguments,我们可以解构出isActive ,这是这个组件提供的。

        <NavLink
          to="/home"
          style={({ isActive }) => ({ color: isActive ? "red" : "green" })}
        >
          首页
        </NavLink>

也可以提供指定className的形式。

        <NavLink
          to="/about"
          className={({ isActive }) => (isActive ? "linkActive" : "")}
        >
          关于
        </NavLink>

这两个方式都可以简单的改变样式。

Navigate组件的使用

Navigate这个组件是用于重定向的。只要他被显示,那么组件就会被重定向。可是实现登录跳转。
我们添加一个Login组件。并做路由配置。

实现登录跳转

import { Routes, Route, Link, NavLink } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
import Login from "./pages/Login";
import "./pages/style.css";

function App() {
  return (
    <div className="App">
      <div>
        <NavLink
          to="/home"
          style={({ isActive }) => ({ color: isActive ? "red" : "green" })}
        >
          首页
        </NavLink>
        <NavLink
          to="/about"
          className={({ isActive }) => (isActive ? "linkActive" : "")}
        >
          关于
        </NavLink>
        <NavLink
          to="/login"
          className={({ isActive }) => (isActive ? "linkActive" : "")}
        >
          登录
        </NavLink>
      </div>
      <div>
        <Routes>
          <Route path="/home" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/login" element={<Login />} />
        </Routes>
      </div>
      <div>Footer</div>
    </div>
  );
}

export default App;

做一个条件判断,只要满足条件,我们就展示Navigate,然后组件就会跳转到指定的路由。

import React from "react";
import { Navigate } from "react-router-dom";
class Login extends React.PureComponent {
  state = {
    isLogin: false,
  };
  handleLogin() {
    this.setState({
      isLogin: true,
    });
  }
  render() {
    const { isLogin } = this.state;
    return (
      <div>
        Login Page
        {!isLogin ? (
          <button onClick={(e) => this.handleLogin()}>登录</button>
        ) : (
          <Navigate to="/home" />
        )}
      </div>
    );
  }
}
export default Login;

路径重定向

我们可以把/重定向到Home。

  <Route path="/" element={<Navigate to="/home" />} />

路由嵌套

也就是二级路由,这个是很常见的。也不难,还是比较方便的。

需要给路由定义子路由。并且在路由是/home的时候,重定向到/home/recommends,这样可以避免,第一次进来二级内容是空白的情况。
App.js

        <Routes>
          <Route path="/" element={<Navigate to="/home" />} />
          <Route path="/home" element={<Navigate to="/home/recommends" />} />
          <Route path="/home" element={<Home />}>
            <Route path="/home/recommends" element={<HomeRecommends />}></Route>
            <Route path="/home/hots" element={<HomeHots />}></Route>
          </Route>
        </Routes>

在具体的页面设置Link组件,并且放一个Outlet组件作为占位符。

      <div>
        Home Page
        <div>
          <Link to="/home/recommends">推荐</Link>
          <Link to="/home/hots">热门</Link>
          <Outlet />
        </div>
      </div>

在这里插入图片描述

通过代码实现路由跳转

有时候,我们需要通过代码实现路由跳转。在router6里面,我们只能通过useNavigate这个hooks来实现代码路由跳转。用了hooks,我们只能使用函数组件。如果一定要在类组件里面实现代码路由跳转,我们只能自定义一个高阶函数withRouter(也是router6的写法)。

使用useNavigate hooks

虽然是通过代码实现路由跳转,但Route还是要手动定义的,这是跳转是通过代码实现的。

          <Route path="/download" element={<Download />} />
          <Route path="/shop" element={<Shop />} />

我们在原来的Link后面添加下面两个元素,我们自己实现点击事件

        <button onClick={(e) => handleNavigate("/download")}>下载</button>
        <span onClick={(e) => handleNavigate("/shop")}>商城</span>

使用useNavigate来实现跳转。这样就已经实现了通过代码实现路由跳转的功能了。

function App() {
  //这行必须写在函数外面
  const navigate = useNavigate();
  function handleNavigate(path) {
    navigate(path);
  }
  return ...div...
}

使用自定义实现高阶函数withRouter

这个自定义的高阶组件,内部也是使用了useNavigate,所以是router6才可以这么用。router5是自带一个withRouter函数的,只是这个函数在router6被移除了。我们自己实现的这个只是叫withRouter这个名字,内部实现是不一样的。

因为useNavigate只能在函数组件里面使用,我们怎么在类组件里面也能用呢?
就是使用高阶函数,中间套一个函数组件,并将navigate 放到props里面,经过这个骚操作,我们就可以在类组件的props里面获取到navigate了。

import { useNavigate } from "react-router-dom";

function withRouter(OriginComponent) {
  return function (props) {
    const navigate = useNavigate();
    const router = { navigate };
    // 套一个router对象,因为可能会封装别的操作
    return <OriginComponent {...props} router={router} />;
  };
}

export default withRouter;

这样,我们就可以通过this.props获取到navigate了。

import React from "react";
import withRouter from "../hoc/withRouter";
import { Outlet } from "react-router-dom";
class Shop extends React.PureComponent {
  handleNavigate(path) {
    const { navigate } = this.props.router;
    navigate(path);
  }
  render() {
    return (
      <div>
        Shop Page
        <div>
          <span onClick={(e) => this.handleNavigate("/shop/hots")}>热卖</span>
          <span> </span>
          <span onClick={(e) => this.handleNavigate("/shop/offsale")}>
            折扣
          </span>
        </div>
        <Outlet />
      </div>
    );
  }
}
export default withRouter(Shop);

App.js定义我们的Route。

          <Route path="/shop" element={<Navigate to="/shop/hots" />} />
          <Route path="/shop" element={<Shop />}>
            <Route path="/shop/hots" element={<ShopHots />} />
            <Route path="/shop/offsale" element={<ShopOffsale />} />
          </Route>

效果就是点击商城,再点击热卖或者折扣,也可以切换路由。
在这里插入图片描述

路由参数传递 动态路由 查询字符

参数传递和动态路由

下面的形式是动态路由的形式,我们可以根据传递的id来显示不同的页面。

<Route path="/shop/detail/:id" element={<ShopDetail />} />

和获取navigate一样,我们可以获取一个params,那么这个params怎么来的呢?我们需要在withRouter里面封装到props里面。

import React from "react";
import withRouter from "../hoc/withRouter";
class ShopDetail extends React.PureComponent {
  render() {
    const { params } = this.props.router;
    console.log(params);

    return <div>ShopDetail id:{params.id}</div>;
  }
}
export default withRouter(ShopDetail);

这样,我们就成功实现把params 封装到props里面了。

import { useNavigate, useParams } from "react-router-dom";

function withRouter(OriginComponent) {
  return function (props) {
    const navigate = useNavigate();
    const params = useParams();
    const router = { navigate, params };
    return <OriginComponent {...props} router={router} />;
  };
}

export default withRouter;

我们的事件发起源是下面这个地方,通过navigate把含有id的路径传递给目标路由。

import React from "react";
import withRouter from "./../hoc/withRouter";
class ShopHots extends React.PureComponent {
  state = {
    goods: [
      { id: "001", name: "商品1" },
      { id: "002", name: "商品2" },
      { id: "003", name: "商品3" },
    ],
  };
  navigateToDetail(id) {
    console.log(id);

    const { navigate } = this.props.router;
    navigate("/shop/detail/" + id);
  }
  render() {
    const { goods } = this.state;
    return (
      <div>
        商店热门
        <div>
          <ul>
            {goods.map((item, index) => {
              return (
                <li onClick={(e) => this.navigateToDetail(item.id)} key={index}>
                  {item.name}
                </li>
              );
            })}
          </ul>
        </div>
      </div>
    );
  }
}
export default withRouter(ShopHots);

最后实现的效果如下:
我们点击具体的商品,跳转到商品详情页。

在这里插入图片描述

查询字符

我们给关于这个Link传点参数。

        <NavLink
          to="/about?user=tom&age=18"
          className={({ isActive }) => (isActive ? "linkActive" : "")}
        >
          关于
        </NavLink>

在withRouter里面通过useSearchParams把query放到props里面。

import {
  useLocation,
  useNavigate,
  useParams,
  useSearchParams,
} from "react-router-dom";

function withRouter(OriginComponent) {
  return function (props) {
    const navigate = useNavigate();
    const params = useParams();
    // const location = useLocation();
    // console.log("location:", location);
    const [searchParams] = useSearchParams();
    const query = Object.fromEntries(searchParams);
    const router = { navigate, params, query };
    return <OriginComponent {...props} router={router} />;
  };
}

export default withRouter;

最后,在目标页面就可以通过props获取到query了。

import React from "react";
import withRouter from "../hoc/withRouter";

class About extends React.PureComponent {
  render() {
    const { query } = this.props.router;
    return <div>About Page:{query.user + " " + query.age}</div>;
  }
}
export default withRouter(About);

路由配置

前面写的路由是通过Route组件来实现的,router6提供了配置文件的形式,估计是参考vue的,本质上,react是把配置文件还是转成我们上面写的Route组件的形式。

现在,我们的Route是这些内容:

      <div>
        <Routes>
          <Route path="/" element={<Navigate to="/home" />} />
          <Route path="/home" element={<Navigate to="/home/recommends" />} />
          <Route path="/home" element={<Home />}>
            <Route path="/home/recommends" element={<HomeRecommends />}></Route>
            <Route path="/home/hots" element={<HomeHots />}></Route>
          </Route>
          <Route path="/about" element={<About />} />
          <Route path="/login" element={<Login />} />
          <Route path="/download" element={<Download />} />
          <Route path="/shop" element={<Navigate to="/shop/hots" />} />
          <Route path="/shop/detail/:id" element={<ShopDetail />} />
          <Route path="/shop" element={<Shop />}>
            <Route path="/shop/hots" element={<ShopHots />} />
            <Route path="/shop/offsale" element={<ShopOffsale />} />
          </Route>
          <Route path="*" element={<NotFound />} />
        </Routes>
      </div>

修改成下面的样子:

import { Navigate } from "react-router-dom";
import Home from "../pages/Home";
import About from "../pages/About";
import Login from "../pages/Login";
import NotFound from "../pages/NotFound";
import HomeHots from "../pages/HomeHots";
import HomeRecommends from "../pages/HomeRecommends";
import Download from "../pages/Download";
import Shop from "../pages/Shop";
import ShopHots from "../pages/ShopHots";
import ShopOffsale from "../pages/ShopOffsale";
import ShopDetail from "../pages/ShopDetail";
const routes = [
  {
    path: "/",
    element: <Navigate to="/home" />,
  },
  {
    path: "/home",
    element: <Navigate to="/home/recommends" />,
  },
  {
    path: "/home",
    element: <Home />,
    children: [
      {
        path: "/home/recommends",
        element: <HomeRecommends />,
      },
      {
        path: "/home/hots",
        element: <HomeHots />,
      },
    ],
  },
  {
    path: "/about",
    element: <About />,
  },
  {
    path: "/login",
    element: <Login />,
  },
  {
    path: "/download",
    element: <Download />,
  },
  {
    path: "/shop",
    element: <Navigate to="/shop/hots" />,
  },
  {
    path: "/shop/detail/:id",
    element: <ShopDetail />,
  },
  {
    path: "/shop",
    element: <Shop />,
    children: [
      {
        path: "/shop/hots",
        element: <ShopHots />,
      },
      {
        path: "/shop/offsale",
        element: <ShopOffsale />,
      },
    ],
  },
  {
    path: "*",
    element: <NotFound />,
  },
];

export default routes;

最后原来的位置提供useRoutes把routes传进去,react就帮我们把配置文件解析好转成Route的形式了。

<div>{useRoutes(routes)}</div>

懒加载分包和Suspense

在不使用懒加载的情况下,打包后的js内容全部打包到main这个js文件里面。
在这里插入图片描述
我们可以通过下面的语句,对页面进行懒加载。import是webpack提供的特性。

const About = React.lazy(() => import("../pages/About"));
const Home = React.lazy(() => import("../pages/Home"));

在使用懒加载后,多出的js文件就是懒加载分包的js文件。
在这里插入图片描述
通常情况下,在加载了懒加载之后,页面会直接崩溃的,但新版本react好像没有这个问题了,如果崩溃,需要添加Suspense,可以有loading的效果,当然fallback可以写空字符串,用户就不会察觉到有loading。

  <Suspense fallback="loading...">
    <HashRouter>
      <App />
    </HashRouter>
  </Suspense>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2253311.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C#】书籍信息的添加、修改、查询、删除

文章目录 一、简介二、程序功能2.1 Book类属性&#xff1a;方法&#xff1a; 2.2 Program 类 三、方法&#xff1a;四、用户界面流程&#xff1a;五、程序代码六、运行效果 一、简介 简单的C#控制台应用程序&#xff0c;用于管理书籍信息。这个程序将允许用户添加、编辑、查看…

打造去中心化交易平台:公链交易所开发全解析

公链交易所&#xff08;Public Blockchain Exchange&#xff09;是指基于公有链&#xff08;如以太坊、波场、币安智能链等&#xff09;建立的去中心化交易平台。与传统的中心化交易所&#xff08;CEX&#xff09;不同&#xff0c;公链交易所基于区块链技术实现资产交换的去中心…

CLIP模型也能处理点云信息

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

关于NXP开源的MCU_boot的项目心得

MCU的启动流程细查 注意MCU上电第一个函数运行的就是Reset_Handler函数&#xff0c;下图是表示了这个函数做了啥事情&#xff0c;注意加强一下对RAM空间的段的印象&#xff0c;从上到下是栈&#xff0c;堆&#xff0c;.bss段&#xff0c;.data段。 bootloader的难点 固件完…

MySQL5.6升级MySQL5.7

升级方式介绍 08 数据库服务版本升级方法 5.6 – 5.7 – 8.0 数据库版本升级方法&#xff1a; Inplace-本地升级 步骤一&#xff1a;在同一台服务器中&#xff0c;需要部署高版本数据库服务实例步骤二&#xff1a;低版本数据库中的数据进行备份迁移&#xff0c;迁移到高版本…

怎么理解BeamSearch?

在大模型推理中&#xff0c;常会用到BeamSearch&#xff0c;本文就BeamSearch原理与应用理解展开讲解。 一、BeamSearch原理 Beam Search 是一种启发式搜索算法&#xff0c;常用于自然语言处理&#xff08;NLP&#xff09;和其他需要生成序列的任务中&#xff0c;比如机器翻译…

shodan2-批量查找CVE-2019-0708漏洞

声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&#…

SciPy Optimize和 CVXPY对比

CVXPY和SciPy Optimize模块都是在Python中解决优化问题的强大工具&#xff0c;但它们是为不同类型的问题而设计的&#xff0c;具有不同的优点和局限性。本文对比两者的优缺点&#xff0c;阐述各自的应用场景&#xff0c;同时解释常用求解器&#xff0c;并给出实际示例进行说明。…

DevOps工程技术价值流:GitLab源码管理与提交流水线实践

在当今快速迭代的软件开发环境中&#xff0c;DevOps&#xff08;开发运维一体化&#xff09;已经成为提升软件交付效率和质量的关键。而GitLab&#xff0c;作为一个全面的开源DevOps平台&#xff0c;不仅提供了强大的版本控制功能&#xff0c;还集成了持续集成/持续交付(CI/CD)…

Android Studio 右侧工具栏 Gradle 不显示 Task 列表

问题&#xff1a; android studio 4.2.1版本更新以后AS右侧工具栏Gradle Task列表不显示&#xff0c;这里需要手动去设置 解决办法&#xff1a; android studio 2024.2.1 Patch 2版本以前的版本设置&#xff1a;依次打开 File -> Settings -> Experimental 选项&#x…

Linux详解:文件权限

文章目录 前言Linux文件权限基础文件成员与三组权限字符 权限的修改修改文件所有者总结 前言 在浩瀚的操作系统世界中&#xff0c;Linux以其开源、灵活和强大的特性&#xff0c;成为了服务器、开发环境以及众多个人用户的首选。而在Linux的众多特性中&#xff0c;文件权限机制…

SeggisV1.0 遥感影像分割软件【源代码】讲解

在此基础上进行二次开发&#xff0c;开发自己的软件&#xff0c;例如&#xff1a;【1】无人机及个人私有影像识别【2】离线使用【3】变化监测模型集成【4】个人私有分割模型集成等等&#xff0c;不管是您用来个人学习还是公司研发需求&#xff0c;都相当合适&#xff0c;包您满…

Spark常问面试题---项目总结

一、数据清洗&#xff0c;你都清洗什么&#xff1f;或者说 ETL 你是怎么做的&#xff1f; 我在这个项目主要清洗的式日志数据&#xff0c;日志数据传过来的json格式 去除掉无用的字段&#xff0c;过滤掉json格式不正确的脏数据 过滤清洗掉日志中缺少关键字段的数据&#xff…

数据结构4——栈和队列

目录 1.栈 1.1.栈的概念及结构 1.2栈的实现 2.队列 2.1队列的概念及结构 2.2队列的实现 1.栈 1.1.栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一段称为栈顶&#xff0c;另一端称为…

限定符使用

正则表达式的元字符一次一般只能匹配一个位置或一个字符,如果想要匹配零个、一个或多个字符时,则需要使用限定符。限定符用于指定允许特定字符或字符集自身重复出现的次数。常用限定符如下: <asp:TextBox [^>]> 正则表达式字符类[^>]匹配除过“>”之外的任何字…

【Python】Selenium模拟在输入框里,一个字一个字地输入文字

我们平常在使用Selenium模拟键盘输入内容&#xff0c;常用的是用send_keys来在输入框上输入字&#xff1a; 基本的输入方式&#xff1a; input_element driver.find_element(By.ID, searchBox) input_element.send_keys("我也爱你") #给骚骚的自己发个骚话不过这种…

Node.js 实战: 爬取百度新闻并序列化 - 完整教程

很多时候我们需要爬取一些公开的网页内容来做一些数据分析和统计。而多数时候&#xff0c;大家会用到python &#xff0c;因为实现起来很方便。但是其实Node.js 用来爬取网络内容&#xff0c;也是非常强大的。 今天我向大家介绍一下我自己写的一个百度新闻的爬虫&#xff0c;可…

配置宝塔php curl 支持http/2 发送苹果apns消息推送

由于宝塔面板默认的php编译的curl未加入http2的支持&#xff0c;如果服务需要使用apns推送等需要http2.0的访问就会失败&#xff0c;所以重新编译php让其支持http2.0 编译方法&#xff1a; 一、安装nghttp2 git clone https://github.com/tatsuhiro-t/nghttp2.git cd nghttp…

YOLOv11 NCNN安卓部署

YOLOv11 NCNN安卓部署 之前自己在验证更换relu激活函数重新训练部署模型的时候&#xff0c;在使用ncnn代码推理验证效果很好&#xff0c;但是部署到安卓上cpu模式会出现大量的错误检测框&#xff0c;现已更换会官方默认的权重 前言 YOLOv11 NCNN安卓部署 目前的帧率可以稳定…

MeterSphere接口测试提取数组及引用

实际工作中常见的使用场景&#xff1a; 1、提取数组中某个特定值&#xff1b; $.data.groups[n].name提取特定值 2、提取数组中全部值&#xff1b; $.data.groups[*].name&#xff0c;并勾选匹配多条以提取全部值 3、提取数组中的某几个特定值&#xff1b; 如提取数组中第1个和…