欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 焦点 > React Router

React Router

2025/5/5 10:34:38 来源:https://blog.csdn.net/2301_82023822/article/details/147520549  浏览:    关键词:React Router

为什么需要路由?

单页应用(SPA):在单页面中实现多视图切换,避免整页刷新。

核心功能

  • 根据 URL 路径渲染对应组件。

  • 实现页面间导航(前进、后退、跳转)。

  • 支持动态路由、嵌套路由、路由守卫等。


一、RouterProvider 核心概念

1. 定位与作用

  • React Router v6.4+ 新增特性:用于替代传统的 <BrowserRouter>,提供 数据驱动路由(Data Routing)能力。

  • 核心能力

    • 集中式路由配置:路由定义与组件解耦。

    • 数据预加载:通过 loader 在路由匹配时自动加载数据。

    • 表单处理:通过 action 处理表单提交。

    • 内置优化:流式渲染(Suspense)、错误边界、滚动恢复等。

  • 适用场景:中大型项目、需复杂数据流管理、服务端渲染(SSR)准备。


二、基础配置与使用

1. 安装依赖

npm install react-router-dom

2. 创建路由配置

// src/router.js
import { createBrowserRouter } from "react-router-dom";
import App from "./App";
import Home from "./pages/Home";
import UserProfile, { loader as userLoader } from "./pages/UserProfile";// 定义路由配置对象
export const router = createBrowserRouter([{path: "/",element: <App />,children: [{ index: true, element: <Home /> },{path: "users/:id",element: <UserProfile />,loader: userLoader, // 数据预加载errorElement: <ErrorPage />, // 错误边界},],},
]);

3. 在入口文件中使用

// src/main.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider } from "react-router-dom";
import { router } from "./router";ReactDOM.createRoot(document.getElementById("root")).render(<React.StrictMode><RouterProvider router={router} /></React.StrictMode>
);

三、数据流管理(loader 与 action

1. loader:路由数据预加载

  • 触发时机:路由匹配时自动调用,在组件渲染前完成数据加载。

  • 典型场景:API 请求、权限校验、数据初始化。

// src/pages/UserProfile.jsx
export async function loader({ params, request }) {// 获取动态参数(如用户ID)const userId = params.id;// 发起数据请求const response = await fetch(`/api/users/${userId}`);if (!response.ok) {throw new Error("用户不存在"); // 抛出错误会被 errorElement 捕获}return response.json();
}function UserProfile() {// 使用 useLoaderData 获取 loader 返回的数据const userData = useLoaderData();return <div>{userData.name}</div>;
}

2. action:处理表单提交

  • 触发时机:表单提交时自动调用(需设置 method="post")。

  • 典型场景:用户注册、数据修改、文件上传。

// src/pages/EditUser.jsx
export async function action({ request, params }) {const formData = await request.formData();const updates = Object.fromEntries(formData);// 提交数据到后端await fetch(`/api/users/${params.id}`, {method: "PUT",body: JSON.stringify(updates),});// 重定向到用户详情页return redirect(`/users/${params.id}`);
}function EditUser() {return (<Form method="post"><input name="name" type="text" /><button type="submit">保存</button></Form>);
}

四、高级功能与最佳实践

1. 错误处理与边界

  • 全局错误捕获:通过路由配置的 errorElement 统一处理。

  • 局部错误处理:在组件内使用 useRouteError Hook。

// 全局错误页面配置
{path: "/users/:id",element: <UserProfile />,loader: userLoader,errorElement: <ErrorBoundary />, // 自定义错误组件
}// ErrorBoundary.jsx
import { useRouteError } from "react-router-dom";function ErrorBoundary() {const error = useRouteError();return (<div><h1>出错了!</h1><p>{error.message}</p></div>);
}

2. 路由懒加载与代码分割

// 结合 React.lazy 和 Suspense 实现懒加载
import { lazy, Suspense } from "react";const Settings = lazy(() => import("./pages/Settings"));const router = createBrowserRouter([{path: "/settings",element: (<Suspense fallback={<div>加载中...</div>}><Settings /></Suspense>),},
]);

3. 权限控制与路由守卫

// 封装鉴权高阶路由配置
function protectedLoader({ request }) {const isAuthenticated = checkAuth();if (!isAuthenticated) {return redirect("/login");}return null;
}const router = createBrowserRouter([{path: "/dashboard",element: <Dashboard />,loader: protectedLoader, // 路由加载前鉴权},
]);

4. 滚动恢复与定位

  • 自动恢复:React Router 默认记录滚动位置。

  • 手动控制:使用 <ScrollRestoration> 组件。

import { ScrollRestoration } from "react-router-dom";function App() {return (<div><ScrollRestoration />{/* 其他内容 */}</div>);
}

五、与传统 BrowserRouter 对比

功能RouterProviderBrowserRouter
路由配置集中式(JSON 结构)分散式(组件树中声明)
数据加载内置 loader,自动预加载需手动使用 useEffect
表单处理内置 action,统一管理提交逻辑需自行处理表单事件和状态
错误处理全局/局部错误边界,自动捕获需手动实现错误边界
代码分割天然支持 Suspense + 懒加载需手动配置
服务端渲染更友好(数据预加载机制)需额外配置

方案一:使用 createBrowserRouter + RouterProvider(推荐)

// router.js
import { createBrowserRouter } from "react-router-dom"
import App from "./App"
import Home from "./pages/home"
import News from "./pages/news"export default createBrowserRouter([{path: '/',element: <App />,children: [{index: true,element: <Home />},{path: '/news',element: <News />}]}
])// App.jsx
import './app.css'
import { Link, Outlet } from 'react-router-dom'function App() {return (<div className='main'><nav><Link to="/">Home</Link> {/* 修改为根路径 */}<Link to="/news">News</Link></nav><div className="content"><Outlet /></div></div>)
}// main.jsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { RouterProvider } from 'react-router-dom'
import router from './router/router'
import { Provider } from 'react-redux'
import store from './store/index.js'createRoot(document.getElementById('root')).render(<StrictMode><Provider store={store}><RouterProvider router={router} /></Provider></StrictMode>
)

方案二:使用 BrowserRouter 组件方式

// main.jsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import store from './store/index.js'
import { Provider } from 'react-redux'
import { BrowserRouter } from 'react-router-dom'createRoot(document.getElementById('root')).render(<StrictMode><Provider store={store}><BrowserRouter><App /></BrowserRouter></Provider></StrictMode>
)// App.jsx
import './app.css'
import { Link, Outlet, Routes, Route } from 'react-router-dom'
import Home from './pages/home'
import News from './pages/news'function App() {return (<div className='main'><nav><Link to="/">Home</Link><Link to="/news">News</Link></nav><div className="content"><Routes><Route path="/" element={<Home />} /><Route path="/news" element={<News />} /></Routes></div></div>)
}

六、常见问题与解决方案

1. 如何传递自定义参数到 loader

  • 方案:通过 URL 参数或搜索参数传递,或在跳转时携带状态:

    navigate("/users/123", { state: { from: "home" } });// loader 中获取
    export async function loader({ request }) {const url = new URL(request.url);const from = url.searchParams.get("from");// 或通过 location.stateconst state = useLocation().state;
    }

2. 如何复用 loader 逻辑?

  • 方案:封装为共享函数:

    // utils/loaders.js
    export async function loadUserData(userId) {const res = await fetch(`/api/users/${userId}`);return res.json();
    }// 路由配置
    import { loadUserData } from "./utils/loaders";
    loader: ({ params }) => loadUserData(params.id)

3. 如何结合 TypeScript 使用?

  • 类型定义:为 loader/action 定义类型:

    import { LoaderFunctionArgs, ActionFunctionArgs } from "react-router-dom";export async function loader({ params }: LoaderFunctionArgs) {// params 自动推断为 { id: string }
    }

七、总结

何时选择 RouterProvider

  • 大型应用:需要集中管理路由和数据流。

  • 复杂数据依赖:多路由共享数据、预加载需求。

  • 优化需求:流式渲染、代码分割、滚动恢复。

  • 未来扩展:计划迁移到服务端渲染(SSR)。

最佳实践

  1. 路由分层管理:拆分为多个路由配置文件。

  2. 严格类型定义:结合 TypeScript 提升安全性。

  3. 合理拆分组件:保持路由配置简洁清晰。

  4. 性能监控:结合 Suspense 和懒加载优化首屏速度。


 

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词