掘金文章归档 · 第 64 篇 React 生态 3/5 React Router · 路由

react 路由基础用法

RouterProvider 配置 → Link/navigate 跳转 → 传参 → 嵌套路由

原文作者:随意_(掘金) | juejin.cn/post/7402137644371329036 | 发布于 2024-08-13 | 阅读 269 · 约 3 分钟

0背景

react 项目中,开发单页面应用,reactRouter 免不了使用,今天记录下路由的配置及使用规范,版本为 "react-router-dom": "^6.26.0"。

1创建测试项目 + 安装路由

npm create vite@latest react-test

选择 react 即可,按提示安装依赖并启动,我这里选择 ts 开发。

npm install react-router-dom -S

2配置路由

创建 pages 及 routers 文件夹,分别放置页面及路由文件。

routers 创建 index.ts 配置路由:

// 导入创建路由的函数
import { createBrowserRouter } from 'react-router-dom';
import NoMatch from "../pages/no-match";
import Login from "../pages/login";

// 创建 router 路由实例对象,并配置路由对应关系(路由数组)
const router = createBrowserRouter([
  {
    // 匹配不到路由
    path: '*',
    Component: NoMatch,
  },
  {
    // 默认路由
    path: '/',
    Component: Login,
  },
  {
    // 登录
    path: '/login',
    Component: Login,
  }
]);

export default router;

在 app.tsx 中引入路由即可:

import { RouterProvider } from "react-router-dom";
import './App.css'

import router from "./routers";

// 渲染应用
function App() {
  return (
    <RouterProvider router={router} fallbackElement={<p>Loading...</p>} />
  );
}

export default App

此时启动项目,可打开登录页,路由匹配不到可到未匹配页面,后面的例子在此项目上实践。

3路由跳转

3.1 声明式导航 Link

登录组件 login index.tsx 代码引入,路由做对应的配置,创建对应测试文件,可实现跳转:

import { Link } from 'react-router-dom';

const Login: React.FC = () => {
  return (
    <div>
      <h2>我是登录页面</h2>
      <Link to="/test1">test1</Link><br></br>
      <Link to="/test2">test2</Link><br></br>
    </div>
  );
};

export default Login;

3.2 编程式导航 navigate

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

const Login: React.FC = () => {
  const navigate = useNavigate();
  return (
    <div>
      <h2>我是登录页面</h2>
      {/* <Link to="/test1">test1</Link><br></br>
      <Link to="/test2">test2</Link><br></br> */}
      <button onClick={() => navigate("/test1")}>test1</button>
      <button onClick={() => navigate("/test2")}>test2</button>
    </div>
  );
};

export default Login;
📌 Link 适合页面里固定入口(渲染为 <a>),navigate 适合事件回调里按逻辑跳转。

4路由传参

4.1 直接传入路径字符串及接收

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

const Login: React.FC = () => {
  const navigate = useNavigate();

  const goTest2 = () => {
    navigate("/test2", { state: { a: 10 } });
  };

  return (
    <div>
      <h2>我是登录页面</h2>
      <button onClick={() => navigate("/test1?param1=value1&param2=value2")}>
        test1
      </button>
      <button onClick={goTest2}>test2</button>
    </div>
  );
};

export default Login;
import { Link, useSearchParams } from 'react-router-dom';

const Test1: React.FC = () => {
  const [params] = useSearchParams();

  return (
    <div>
      <h2>Test1</h2>
      <Link to="/">首页</Link><br></br>
      <p>传参:{params.get('param1')}</p>
    </div>
  );
};

export default Test1;

字符串传参,直接在 navigate 拼接即可,useSearchParams 来接收参数。

4.2 Location State

import { Link, useLocation } from "react-router-dom";

const Test2: React.FC = () => {
  const { state } = useLocation();

  return (
    <div>
      <h2>Test2</h2>
      <Link to="/">首页</Link>
      <br></br>
      <p>接收参数:{state.a}</p>
    </div>
  );
};

export default Test2;
const goTest2 = () => {
  navigate("/test2", { state: { a: 10 } });
};

goTest2 使用 Location State 传参,useLocation 来接收参数。

⚠️ 两种传参别混:query 字符串用 useSearchParams(刷新不丢),对象数据用 state(页面刷新后 state 会丢失,需注意兜底)。

5路由嵌套

在一级路由中又内嵌了其他路由,这种关系就叫做嵌套路由;嵌套至一级路由内的路由又称为二级路由。

// 导入创建路由的函数
import { createBrowserRouter } from "react-router-dom";
import NoMatch from "../pages/no-match";
import Login from "../pages/login";
import Test2 from "../pages/test2";
import Test1Layout from "../pages/test1/test-layout";
import Test1 from "../pages/test1";
import Test11 from "../pages/test1/test1-1";

// 创建 router 路由实例对象,并配置路由对应关系(路由数组)
const router = createBrowserRouter([
  {
    // 匹配不到路由
    path: "*",
    Component: NoMatch,
  },
  {
    // 默认路由
    path: "/",
    Component: Login,
  },
  {
    // 登录
    path: "/login",
    Component: Login,
  },
  {
    path: "/test1layout",
    Component: Test1Layout,
    children: [
      // 二级路由默认路由
      { index: true, Component: Test1 },
      {
        path: "test1",
        Component: Test1,
      },
      {
        path: "test11",
        Component: Test11,
      },
    ],
  },
  {
    // 测试2
    path: "/test2",
    Component: Test2,
  },
]);

export default router;
import { Outlet } from "react-router-dom";

const Test1Layout: React.FC = () => {
  return (
    <div>
      <h2>Test1 layout</h2>
      <Outlet />
    </div>
  );
};

export default Test1Layout;
📌 children 里配 index: true 表示一级路由默认渲染的二级路由;Outlet 放哪,二级路由就渲染在哪。

6navigate 传参及 useLocation 获取参数

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

const Home = () => {
  const navigate = useNavigate();

  const handleClick = () => {
    const params = {
      id: 1,
      name: "Example Knowledge",
    };
    navigate("/knowlegeBase", { state: params });
  };

  return (
    <div>
      <button onClick={handleClick}>Go to Knowledge Base</button>
    </div>
  );
};

export default Home;
import React from "react";
import { useLocation } from "react-router-dom";

const KnowledgeBase = () => {
  const location = useLocation();
  const params = location.state;

  return (
    <div>
      <h1>Knowledge Base</h1>
      {params && (
        <div>
          <p>ID: {params.id}</p>
          <p>Name: {params.name}</p>
        </div>
      )}
    </div>
  );
};

export default KnowledgeBase;
📌 完整链路:navigate(url, { state }) 携带对象 → useLocation().state 取出 → 渲染前先判空({params && ...})防止刷新后 undefined 报错。