中文

探索 React Router v6 的核心导航模式。学习声明式路由、动态路由、编程式导航、嵌套路由和数据加载策略,以构建功能强大且用户友好的 Web 应用程序。

React Router v6:掌握现代 Web 应用的导航模式

React Router v6 是一个功能强大且灵活的 React 应用路由库。它允许您通过管理导航来创建单页应用(SPA),无需整页刷新,从而提供无缝的用户体验。这篇博文将深入探讨使用 React Router v6 的核心导航模式,为您提供构建健壮且用户友好的 Web 应用所需的知识和示例。

理解 React Router v6 的核心概念

在深入探讨具体模式之前,让我们先回顾一些基本概念:

1. 使用 <Routes><Route> 进行声明式路由

React Router v6 的基础在于声明式路由。您使用 <Routes><Route> 组件来定义路由。<Routes> 组件作为路由的容器,而 <Route> 组件则定义一个特定的路由以及当该路由与当前 URL 匹配时要渲染的组件。

示例:基本路由配置

这是一个为一个简单应用设置路由的基本示例:


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

function App() {
  return (
    
      
        } />
        } />
        } />
      
    
  );
}

export default App;

在这个示例中,我们定义了三个路由:

BrowserRouter 组件启用了基于浏览器历史记录的路由。React Router 会将当前 URL 与定义的路由进行匹配,并渲染相应的组件。

2. 带 URL 参数的动态路由

动态路由允许您创建可以处理 URL 中不同值的路由。这对于根据唯一标识符(如产品 ID 或用户 ID)显示内容非常有用。React Router v6 使用 : 符号来定义 URL 参数。

示例:显示产品详情

假设您有一个电子商务应用,并希望根据每个产品的 ID 显示其详情。您可以像这样定义一个动态路由:


import { BrowserRouter, Routes, Route, useParams } from "react-router-dom";

function ProductDetails() {
  const { productId } = useParams();

  // 根据 productId 获取产品详情
  // ...

  return (
    

产品详情

产品 ID: {productId}

{/* 在此处显示产品详情 */}
); } function App() { return ( } /> ); } export default App;

在这个示例中:

国际化示例:处理语言代码

对于一个多语言网站,您可能会使用动态路由来处理语言代码:


} />

这个路由会匹配像 /en/about/fr/about/es/about 这样的 URL。然后可以使用 lang 参数来加载相应的语言资源。

3. 使用 useNavigate 进行编程式导航

虽然声明式路由对于静态链接非常棒,但您经常需要根据用户操作或应用逻辑进行编程式导航。React Router v6 为此提供了 useNavigate 钩子。useNavigate 返回一个函数,允许您导航到不同的路由。

示例:表单提交后重定向

假设您有一个表单提交,并希望在表单成功提交后将用户重定向到一个成功页面:


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

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

  const handleSubmit = async (event) => {
    event.preventDefault();

    // 提交表单数据
    // ...

    // 成功提交后重定向到成功页面
    navigate("/success");
  };

  return (
    
{/* 表单字段 */}
); } export default MyForm;

在这个示例中:

在导航期间传递状态

您还可以使用 navigate 的第二个参数在导航时传递状态:


navigate("/confirmation", { state: { orderId: "12345" } });

这允许您将数据传递给目标组件,目标组件可以使用 useLocation 钩子来访问这些数据。

4. 嵌套路由和布局

嵌套路由允许您创建分层的路由结构,即一个路由嵌套在另一个路由中。这对于组织具有多层导航的复杂应用非常有用。这有助于创建布局,使得某些 UI 元素在应用的某个部分中保持一致。

示例:用户个人资料部分

假设您有一个用户个人资料部分,其中包含用于显示用户个人信息、设置和订单的嵌套路由:


import { BrowserRouter, Routes, Route, Link } from "react-router-dom";

function Profile() {
  return (
    

用户个人资料

  • 个人信息
  • 设置
  • 订单
} /> } /> } />
); } function ProfileInformation() { return

个人信息组件

; } function Settings() { return

设置组件

; } function Orders() { return

订单组件

; } function App() { return ( } /> ); } export default App;

在这个示例中:

父路由中的 * 至关重要;它表示父路由应匹配任何子路径,从而允许嵌套路由在 Profile 组件内被正确匹配。

5. 处理“未找到”(404)错误

处理用户导航到不存在的路由的情况至关重要。React Router v6 通过一个“全匹配”路由使这变得容易。

示例:实现一个 404 页面


import { BrowserRouter, Routes, Route, Link } from "react-router-dom";

function NotFound() {
  return (
    

404 - 未找到

您正在寻找的页面不存在。

返回首页
); } function App() { return ( } /> } /> } /> ); }

在这个示例中:

6. React Router v6 的数据加载策略

React Router v6 不像其前身(带有 `useRouteMatch` 的 React Router v5)那样包含内置的数据加载机制。但是,它提供了有效实现各种数据加载策略的工具。

选项 1:在组件中获取数据

最简单的方法是在渲染路由的组件内部直接获取数据。您可以使用 useEffect 钩子在组件挂载或 URL 参数更改时获取数据。


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

function ProductDetails() {
  const { productId } = useParams();
  const [product, setProduct] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchProduct() {
      try {
        const response = await fetch(`/api/products/${productId}`);
        if (!response.ok) {
          throw new Error(`HTTP 错误!状态: ${response.status}`);
        }
        const data = await response.json();
        setProduct(data);
        setLoading(false);
      } catch (e) {
        setError(e);
        setLoading(false);
      }
    }

    fetchProduct();
  }, [productId]);

  if (loading) return 

加载中...

; if (error) return

错误: {error.message}

; if (!product) return

未找到产品

; return (

{product.name}

{product.description}

); } export default ProductDetails;

这种方法很直接,但如果需要在多个组件中获取数据,可能会导致代码重复。效率也较低,因为数据获取只有在组件挂载后才开始。

选项 2:使用自定义 Hook 获取数据

为了减少代码重复,您可以创建一个封装数据获取逻辑的自定义 Hook。然后可以在多个组件中重用这个 Hook。


import { useState, useEffect } from "react";

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchData() {
      try {
        const response = await fetch(url);
        if (!response.ok) {
          throw new Error(`HTTP 错误!状态: ${response.status}`);
        }
        const json = await response.json();
        setData(json);
        setLoading(false);
      } catch (e) {
        setError(e);
        setLoading(false);
      }
    }

    fetchData();
  }, [url]);

  return { data, loading, error };
}

export default useFetch;

然后,您可以在您的组件中使用这个 Hook:


import { useParams } from "react-router-dom";
import useFetch from "./useFetch";

function ProductDetails() {
  const { productId } = useParams();
  const { data: product, loading, error } = useFetch(`/api/products/${productId}`);

  if (loading) return 

加载中...

; if (error) return

错误: {error.message}

; if (!product) return

未找到产品

; return (

{product.name}

{product.description}

); } export default ProductDetails;

选项 3:使用具备数据获取能力的路由库(TanStack Router, Remix)

TanStack RouterRemix 这样的库提供了与路由无缝集成的内置数据获取机制。这些库通常提供以下功能:

使用这样的库可以极大地简化数据加载并提高性能,特别是对于复杂的应用程序。

服务器端渲染(SSR)和静态站点生成(SSG)

为了改善 SEO 和初始加载性能,可以考虑使用像 Next.js 或 Gatsby 这样的框架进行 SSR 或 SSG。这些框架允许您在服务器上或在构建时获取数据,并向客户端提供预渲染的 HTML。这消除了客户端在初始加载时获取数据的需要,从而带来了更快、更友好的 SEO 体验。

7. 使用不同类型的路由器

React Router v6 提供了不同的路由器实现,以适应各种环境和用例:

选择最适合您应用需求和环境的路由器类型。

结论

React Router v6 为 React 应用提供了全面而灵活的路由解决方案。通过理解和应用本博文中讨论的导航模式,您可以构建健壮、用户友好且易于维护的 Web 应用。从使用 <Routes><Route> 的声明式路由,到带 URL 参数的动态路由、使用 useNavigate 的编程式导航,以及有效的数据加载策略,React Router v6 使您能够为用户创建无缝的导航体验。考虑探索高级路由库和 SSR/SSG 框架以获得更强的控制和性能优化。请记住,要根据您的特定应用需求调整这些模式,并始终优先考虑清晰直观的用户体验。