React Router 6的学习

安装react-router-dom

复制代码
npm i react-router-dom

支持不同的路由创建

createBrowserRouter

特点

  1. 推荐使用 的方式,基于 HTML5 的 History API。
  2. 支持用户友好的 URL,无需 #
  3. 适用于生产环境的绝大多数场景。

适用

使用现代浏览器,支持 pushState 和 replaceState 的情况

应用

javascript 复制代码
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Home from "../views/Home";
import About from "../views/About";
import ErrorPage from "../views/ErrorPage";

const router = createBrowserRouter([
  { path: "/", element: <Home /> },
  { path: "/about", element: <About /> },
  {
    path: "*", // 404 页面
    element: <ErrorPage />,
  },
]);

function App() {
  return <RouterProvider router={router} />;
}

export default App;

tips

  1. 需要服务器支持,确保非根路径的页面请求被正确处理(常见问题是刷新后出现 404)
  2. 部署时,服务器通常需要配置以处理 SPA 应用

createHashRouter

特点

  1. URL 使用 #,如 http://example.com/#/about
  2. 不依赖服务器配置,适合快速开发和简单项目。
  3. 不推荐 ,因为URL 不够优雅,且 SEO 支持较差。

场景

  1. 静态站点部署(如 GitHub Pages)。
  2. 不支持 History API 的环境。

应用

javascript 复制代码
import { createHashRouter, RouterProvider } from "react-router-dom";
import Home from "../views/Home";
import About from "../views/About";

const router = createHashRouter([
  { path: "/", element: <Home /> },
  { path: "/about", element: <About /> },
]);

function App() {
  return <RouterProvider router={router} />;
}

export default App;

tips

  1. 无需服务器配置,但不适合对 URL 有美观或 SEO 要求的项目

createMemoryRouter

特点

  1. 不依赖浏览器环境,维护自己的内存中的路由栈。
  2. URL 不显示在浏览器地址栏
  3. 常用于非浏览器环境(如 React Native)或开发工具(如 Storybook)

场景

  1. 测试环境。
  2. 开发工具或需要自定义路由栈的应用

应用

javascript 复制代码
import { createMemoryRouter, RouterProvider } from "react-router-dom";
import Home from "../views/Home";
import About from "../views/About";

const router = createMemoryRouter([
  { path: "/", element: <Home /> },
  { path: "/about", element: <About /> },
]);

function App() {
  return <RouterProvider router={router} />;
}

export default App;

tips

  1. 开发中调试时有用,但生产环境通常不使用。
  2. URL 不可见,不适合需要 SEO 的场景。

createStaticRouter

特点

  1. 用于服务器端渲染(SSR)。
  2. 基于预定义的静态路由配置。
  3. 无需用户交互,生成的路由用于服务端直接渲染

场景

  1. SSR(服务器端渲染)应用,例如使用 React Router 与 Next.js 或 Remix

应用

javascript 复制代码
import { createStaticRouter, StaticRouterProvider } from "react-router-dom/server";
import Home from "../views/Home";
import About from "../views/About";

const router = createStaticRouter([
  { path: "/", element: <Home /> },
  { path: "/about", element: <About /> },
]);

function App() {
  return <StaticRouterProvider router={router} />;
}

export default App;

tips

  1. createStaticRouter需要结合服务器环境。
  2. 主要用于预渲染生成的 HTML,例如静态页面生成器或 SSR 服务。

总结

|---------------------|---------------------------|------------------|------------------|
| 路由方式 | 特点 | 适用场景 | 优缺点 |
| createBrowserRouter | 基于 HTML5 History API,推荐使用 | 现代浏览器应用,生产环境 | URL 美观,需服务器支持 |
| createHashRouter | URL 包含 #,不依赖服务器 | 快速开发、简单项目,静态站点部署 | URL 不优雅,SEO 支持较差 |
| createMemoryRouter | 路由存储在内存中,无需浏览器环境 | 测试环境、开发工具,无浏览器场景 | 不适用于生产环境,URL 不可见 |
| createStaticRouter | 静态路由,用于 SSR 和静态页面生成 | 服务器端渲染 | 依赖服务端,无客户端交互 |

路由配置

  1. 使用 RoutesRoute 代替 React Router 5 的 Switch 和路由定义方式。
  2. 通过嵌套路由支持嵌套组件,可以实现更清晰的层次结构。
  3. path 属性定义路由路径,element 属性指定要渲染的组件
javascript 复制代码
// App.jsx

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/user/:id" element={<User />} />
      </Routes>
    </Router>
  );
}

function Home() {
  return <h1>Home Page</h1>;
}

function About() {
  return <h1>About Page</h1>;
}

function User({ id }) {
  return <h1>User Page for {id}</h1>;
}

动态路由

  1. 动态路由通过 :paramName 定义路径参数。
  2. 使用 useParams 钩子获取参数值。
  3. 动态路由使得可以根据 URL 参数渲染不同的内容,useParams 是获取路径参数的关键。
javascript 复制代码
import { useParams } from 'react-router-dom';

function User() {
  const { id } = useParams(); // 获取路径中的 id 参数
  return <h1>User ID: {id}</h1>;
}

导航

  1. 使用 useNavigate 替代旧版本中的 useHistory
  2. useNavigate 提供更灵活的导航方式。
  3. navigate 可以接受路径字符串,也可以接受对象形式,适合处理编程式导航
javascript 复制代码
import { useNavigate } from 'react-router-dom';

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

  const goToAbout = () => {
    navigate('/about');
  };

  return (
    <div>
      <h1>Home Page</h1>
      <button onClick={goToAbout}>Go to About</button>
    </div>
  );
}

嵌套路由

  1. 支持组件内部嵌套路由。
  2. 子路由通过 Outlet 渲染。
  3. Outlet 是子路由的占位符,嵌套路由更适合复杂布局。
javascript 复制代码
import { Outlet, Link } from 'react-router-dom';

function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      <nav>
        <Link to="profile">Profile</Link>
        <Link to="settings">Settings</Link>
      </nav>
      <Outlet /> {/* 渲染子路由 */}
    </div>
  );
}

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="dashboard" element={<Dashboard />}>
          <Route path="profile" element={<Profile />} />
          <Route path="settings" element={<Settings />} />
        </Route>
      </Routes>
    </Router>
  );
}

重定向

  1. 使用 Navigate 组件进行页面重定向。
  2. 可在组件渲染时动态重定向。
  3. Navigate 是一个 JSX 组件,用于在路由中实现条件跳转
javascript 复制代码
import { Navigate } from 'react-router-dom';

function ProtectedRoute({ isAuth, children }) {
  return isAuth ? children : <Navigate to="/login" />;
}

function App() {
  const isAuth = false; // 假设用户未登录

  return (
    <Router>
      <Routes>
        <Route
          path="/protected"
          element={
            <ProtectedRoute isAuth={isAuth}>
              <ProtectedPage />
            </ProtectedRoute>
          }
        />
        <Route path="/login" element={<Login />} />
      </Routes>
    </Router>
  );
}

数据加载

  1. React Router 6.4+ 引入了 loaderuseLoaderData,用于预加载路由数据。
  2. 支持异步加载,优化页面渲染体验。
  3. loader 提供异步数据加载,提升数据获取的灵活性,useLoaderData 获取加载的数据。
javascript 复制代码
import { createBrowserRouter, RouterProvider, useLoaderData } from 'react-router-dom';

function UserProfile() {
  const data = useLoaderData(); // 获取预加载的数据
  return <h1>User Name: {data.name}</h1>;
}

const router = createBrowserRouter([
  {
    path: "/user/:id",
    element: <UserProfile />,
    loader: async ({ params }) => {
      const response = await fetch(`/api/user/${params.id}`);
      return response.json();
    },
  },
]);

function App() {
  return <RouterProvider router={router} />;
}

路由懒加载

  1. 减少首屏加载时间,提高性能;按需加载组件,降低内存占用;提升用户体验,特别是对于大型应用。
  2. 首次加载某个路由可能会有短暂延迟;如果 fallback 设计不当,可能导致用户体验下降;对 SEO 不友好,需配合服务器端渲染(SSR)解决。
  3. React.lazy(() => import('./ComponentPath')) 实现组件按需加载。
  4. Suspense用于显示加载状态(如 Loading...)直到懒加载组件加载完成
javascript 复制代码
import React from "react";

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

const routes = [
  {
    path: "/",
    element: (
      <React.Suspense fallback={<div>Loading...</div>}>
        <Home />
      </React.Suspense>
    ),
  },
  {
    path: "/about",
    element: (
      <React.Suspense fallback={<div>Loading...</div>}>
        <About />
      </React.Suspense>
    ),
  },
  {
    path: "/dashboard",
    element: (
      <React.Suspense fallback={<div>Loading...</div>}>
        <Dashboard />
      </React.Suspense>
    ),
  },
];

export default routes;
javascript 复制代码
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import routes from "./routes";

const router = createBrowserRouter(routes);

function App() {
  return <RouterProvider router={router} />;
}

export default App;

路由保护

  1. 使用高阶组件模式或条件渲染实现路由保护。
  2. 常与 Navigate 和认证逻辑结合。
javascript 复制代码
function ProtectedRoute({ isAuth, children }) {
  return isAuth ? children : <Navigate to="/login" />;
}

错误处理

  1. 支持 errorElement 处理路由级别的错误。
javascript 复制代码
const router = createBrowserRouter([
  {
    path: "/",
    element: <Home />,
    errorElement: <ErrorPage />, // 错误处理组件
  },
]);

function ErrorPage() {
  return <h1>Something went wrong!</h1>;
}

综合案例

javascript 复制代码
src/
├── router/
│   └── index.jsx  // 路由配置
├── views/
│   ├── Home.jsx   // 首页
│   ├── Login.jsx  // 登录页
│   ├── Content.jsx  // 内容页 (嵌套路由)
│   ├── ErrorPage.jsx // 错误页面
│   └── Protected.jsx // 受保护的页面
├── App.jsx        // 应用入口
└── main.jsx       // 渲染入口
javascript 复制代码
// src/router/index.jsx
import { createBrowserRouter } from "react-router-dom";
import Home from "../views/Home";
import Login from "../views/Login";
import ErrorPage from "../views/ErrorPage";
import Content from "../views/Content";
import Protected from "../views/Protected";
import { Navigate } from "react-router-dom";

const router = createBrowserRouter([
  {
    path: "/",
    element: <Navigate to="/home" replace />, // 重定向到 /home
  },
  {
    path: "/home",
    element: <Home />,
    errorElement: <ErrorPage />,
    children: [
      {
        path: "content",
        element: <Content />,
      },
    ],
  },
  {
    path: "/login",
    element: <Login />,
    errorElement: <ErrorPage />,
  },
  {
    path: "/protected",
    element: <ProtectedRoute>
      <Protected />
    </ProtectedRoute>,
    errorElement: <ErrorPage />,
  },
  {
    path: "*", // 404 页面
    element: <ErrorPage />,
  },
]);

// 模拟受保护的路由
function ProtectedRoute({ children }) {
  const isAuthenticated = false; // 假设用户未登录
  return isAuthenticated ? children : <Navigate to="/login" replace />;
}

export default router;

页面组件:src/views

javascript 复制代码
// Home.jsx
import { Outlet, Link } from "react-router-dom";

function Home() {
  return (
    <div>
      <h1>Home Page</h1>
      <nav>
        <Link to="/home/content">Go to Content</Link>
        <Link to="/protected">Go to Protected Page</Link>
      </nav>
      <Outlet /> {/* 嵌套路由 */}
    </div>
  );
}

export default Home;
javascript 复制代码
//Login.jsx
import { useNavigate } from "react-router-dom";

function Login() {
  const navigate = useNavigate();

  const handleLogin = () => {
    // 模拟登录操作
    alert("Logged in!");
    navigate("/home"); // 登录后跳转到首页
  };

  return (
    <div>
      <h1>Login Page</h1>
      <button onClick={handleLogin}>Login</button>
    </div>
  );
}

export default Login;
javascript 复制代码
//Content.jsx
function Content() {
  return <h1>Content Page</h1>;
}

export default Content;
javascript 复制代码
//Protected.jsx
function Protected() {
  return <h1>Protected Page: You are authenticated!</h1>;
}

export default Protected;
javascript 复制代码
//ErrorPage.jsx
function ErrorPage() {
  return <h1>404 Not Found</h1>;
}

export default ErrorPage;
javascript 复制代码
// src/App.js
import { RouterProvider } from "react-router-dom";
import router from "./router";

function App() {
  return (
    <div>
      <RouterProvider router={router} />
    </div>
  );
}

export default App;
javascript 复制代码
// src/index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

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

效果

相关推荐
Aress"12 分钟前
uniapp设置vuex公共值状态管理
javascript·vue.js·uni-app
东芃939416 分钟前
uniapp上传blob对象到后台
前端·javascript·uni-app
贝西奇谈39 分钟前
JavaScript DOM节点操作详解
开发语言·javascript·php
野老杂谈1 小时前
如何快速学习智能合约开发语言 Solidity
开发语言·学习·智能合约·solidity·以太坊·区块链开发
Han.miracle1 小时前
Java线程的学习—多线程(一)
java·开发语言·学习
忧郁奔向冷的天1 小时前
视觉SLAM十四讲2nd—学习笔记(二)20250817
笔记·学习
reoreo2 小时前
如何使用 i18next 实现多种语言的国际化(从新建 vite ts 项目开始)
前端·javascript
咸虾米2 小时前
在unicloud的云对象中如何调用同一服务空间内的另外其他云对象
javascript·微信小程序·前端框架
南山安2 小时前
从零开始玩转 AIGC:用 Node.js 调用 OpenAI 接口实现图像生成与销售数据分析
javascript·node.js
Asort2 小时前
JavaScript设计模式(二十三)——访问者模式:优雅地扩展对象结构
前端·javascript·设计模式