React Router 三种模式深度对比:Framework Mode vs Data Mode vs Declarative Mode
深入对比 React Router 的三种使用模式:Framework Mode(框架模式)、Data Mode(数据模式)、Declarative Mode(声明模式),帮助你选择最适合项目的路由方案。
React Router 三种模式深度对比:Framework Mode vs Data Mode vs Declarative Mode
React Router 作为 React 生态系统中最流行的路由解决方案,在最新版本中提供了三种不同的使用模式:Framework Mode(框架模式)、Data Mode(数据模式)、Declarative Mode(声明模式)。每种模式都有其独特的优势和适用场景。本文将深入对比这三种模式,帮助你做出明智的选择。
什么是 React Router 模式?
React Router 的模式代表了不同的抽象层次和控制权分配。从简单到复杂,从灵活到完整,三种模式为不同需求的开发者提供了合适的选择。
重要提示:所有模式都支持任何架构和部署目标(SPA、SSR、SSG 等)。选择模式的关键不在于你想要什么样的架构,而在于你希望 React Router 提供多少帮助。
1. 声明式模式(Declarative Mode)
概述
声明式模式是最基础、最直接的路由使用方式。它提供了核心的路由功能,让你能够快速上手 React Router。
核心特性
- ✅ URL 到组件的映射
- ✅ 应用内导航
- ✅ 活动状态管理
- ✅ 基本的
<Link>、useNavigate和useLocationAPI - ✅ 简单易用,学习曲线平缓
代码示例
import { BrowserRouter, Routes, Route, Link, useNavigate } from 'react-router';
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
<Link to="/contact">联系</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</BrowserRouter>
);
}
function Home() {
const navigate = useNavigate();
return (
<div>
<h1>首页</h1>
<button onClick={() => navigate('/about')}>
前往关于页面
</button>
</div>
);
}
适用场景
- 从 React Router v6 迁移的项目
- 简单的单页应用(SPA)
- 不需要复杂数据加载的场景
- 希望保持对项目架构完全控制的开发者
2. 数据模式(Data Mode)
概述
数据模式在声明式模式的基础上,增加了强大的数据加载和操作能力。它将路由配置移出 React 渲染,提供了更高效的数据管理方式。
核心特性
- ✅ 所有声明式模式的功能
- ✅ Loader - 路由级别的数据加载
- ✅ Action - 表单提交和数据修改
- ✅ useFetcher - 不阻塞导航的数据获取
- ✅ 待定状态(Pending States)管理
- ✅ 错误边界处理
- ✅ 保持对打包和服务器抽象的控制
代码示例
import {
createBrowserRouter,
RouterProvider,
useLoaderData,
useFetcher,
} from 'react-router';
// 路由配置
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{
path: "users/:userId",
element: <UserProfile />,
loader: async ({ params }) => {
// 在路由渲染前加载数据
const user = await fetchUser(params.userId);
return { user };
},
action: async ({ request }) => {
// 处理表单提交
const formData = await request.formData();
const result = await updateUser(formData);
return result;
}
}
]
}
]);
function UserProfile() {
const { user } = useLoaderData();
const fetcher = useFetcher();
return (
<div>
<h1>{user.name}</h1>
<fetcher.Form method="post">
<input name="email" defaultValue={user.email} />
<button type="submit">更新</button>
</fetcher.Form>
</div>
);
}
function App() {
return <RouterProvider router={router} />;
}
适用场景
- 需要复杂数据加载的应用
- 希望利用数据功能但保持架构控制
- 需要处理表单提交和数据修改
- 想要更好的加载状态管理
3. 框架模式(Framework Mode)
概述
框架模式是最高级别的抽象,通过 Vite 插件包装了数据模式,提供了完整的 React Router 开发体验。这是最强大但也最”固执己见”的模式。
核心特性
- ✅ 所有数据模式的功能
- ✅ 类型安全的 href - 编译时检查路由链接
- ✅ 类型安全的路由模块 API - 完整的 TypeScript 支持
- ✅ 智能代码分割 - 自动优化打包
- ✅ SPA、SSR 和静态渲染 - 多种渲染策略
- ✅ Vite 插件集成 - 开箱即用的开发体验
- ✅ 文件系统路由 - 类似 Next.js 的路由约定
代码示例
// 使用 Vite 插件配置
// vite.config.js
import { reactRouter } from "@react-router/dev/vite";
export default {
plugins: [
reactRouter({
// 自动扫描路由文件
routes: async () => {
return [
{
path: "/",
file: "routes/home.tsx",
},
{
path: "/users/:userId",
file: "routes/users.$userId.tsx",
}
];
}
})
]
};
// routes/users.$userId.tsx
import { json } from "react-router";
import type { Route } from "./+types/users.$userId";
// 类型安全的数据加载
export async function loader({ params }: Route.LoaderArgs) {
const user = await fetchUser(params.userId);
return json({ user });
}
// 类型安全的组件
export default function UserProfile({ loaderData }: Route.ComponentProps) {
return <div>{loaderData.user.name}</div>;
}
适用场景
- 全新的 React 项目
- 需要完整的框架功能
- 希望最大化开发效率
- 需要 SSR 或静态生成
- 团队可以接受框架的约定
API 可用性对比
这是官方提供的 API 在不同模式下的可用性表格,帮助你了解每种模式的功能范围:
组件 API
| API | 框架模式 | 数据模式 | 声明式模式 |
|---|---|---|---|
<Link> | ✅ | ✅ | ✅ |
<NavLink> | ✅ | ✅ | ✅ |
<Form> | ✅ | ✅ | ❌ |
<Outlet> | ✅ | ✅ | ✅ |
<Navigate> | ✅ | ✅ | ✅ |
<Await> | ✅ | ✅ | ❌ |
<Meta> | ✅ | ✅ | ❌ |
<Scripts> | ✅ | ✅ | ❌ |
<ScrollRestoration> | ✅ | ✅ | ❌ |
Hooks API
| API | 框架模式 | 数据模式 | 声明式模式 |
|---|---|---|---|
useNavigate | ✅ | ✅ | ✅ |
useLocation | ✅ | ✅ | ✅ |
useParams | ✅ | ✅ | ✅ |
useSearchParams | ✅ | ✅ | ✅ |
useLoaderData | ✅ | ✅ | ❌ |
useActionData | ✅ | ✅ | ❌ |
useNavigation | ✅ | ✅ | ❌ |
useFetcher | ✅ | ✅ | ❌ |
useFetchers | ✅ | ✅ | ❌ |
useSubmit | ✅ | ✅ | ❌ |
useRevalidator | ✅ | ✅ | ❌ |
useRouteError | ✅ | ✅ | ❌ |
useRouteLoaderData | ✅ | ✅ | ❌ |
useMatches | ✅ | ✅ | ❌ |
useAsyncError | ✅ | ✅ | ❌ |
useAsyncValue | ✅ | ✅ | ❌ |
useBlocker | ✅ | ✅ | ❌ |
usePrompt | ✅ | ✅ | ❌ |
useViewTransitionState | ✅ | ✅ | ❌ |
工具函数
| API | 框架模式 | 数据模式 | 声明式模式 |
|---|---|---|---|
redirect | ✅ | ✅ | ❌ |
redirectDocument | ✅ | ✅ | ❌ |
json | ✅ | ✅ | ❌ |
defer | ✅ | ✅ | ❌ |
createCookie | ✅ | ✅ | ❌ |
createCookieSessionStorage | ✅ | ✅ | ❌ |
createMemorySessionStorage | ✅ | ✅ | ❌ |
三种模式对比
| 特性 | 声明式模式 | 数据模式 | 框架模式 |
|---|---|---|---|
| 学习曲线 | ⭐ 简单 | ⭐⭐ 中等 | ⭐⭐⭐ 较陡 |
| 配置复杂度 | 低 | 中 | 高 |
| 数据加载 | ❌ 无 | ✅ Loader | ✅ Loader |
| 表单处理 | ❌ 手动 | ✅ Action | ✅ Action |
| 类型安全 | 基础 | 基础 | ✅ 完整 |
| 代码分割 | 手动 | 手动 | ✅ 自动 |
| SSR 支持 | ❌ | 需要配置 | ✅ 开箱即用 |
| 控制权 | ⭐⭐⭐ 完全控制 | ⭐⭐ 部分控制 | ⭐ 框架控制 |
| 迁移难度 | - | 中等 | 困难 |
| 推荐场景 | 简单 SPA | 数据密集型应用 | 全栈应用 |
如何选择?
根据官方文档的建议,选择模式的关键在于你希望自己完成多少工作,以及你希望 React Router 提供多少帮助。
选择框架模式,如果你:
- 🆕 太新而没有明确偏好 - 刚开始接触 React Router
- 🔄 正在考虑其他框架 - 在 Next.js、Solid Start、SvelteKit、Astro、TanStack Start 等框架之间比较
- 🚀 只想用 React 构建应用 - 不需要太多考虑架构细节
- ❓ 不确定是否需要 SSR - 可能想要服务端渲染,也可能不需要
- 📦 从 Remix 迁移 - React Router v7 是 Remix v2 的”下一版本”
- 🔧 从 Next.js 迁移 - 想要类似的开发体验
推荐:直接使用框架模式开始你的项目。
选择数据模式,如果你:
- 💾 想要数据功能但保持控制 - 需要数据加载和操作,但同时想控制打包、数据和服务器抽象
- ✅ 已在 v6.4 使用数据路由 - 从 v6.4 开始使用数据路由器并且对当前方案满意
推荐:继续使用数据模式,享受数据功能的同时保持架构控制。
选择声明式模式,如果你:
- 🎯 追求极简使用 - 希望尽可能简单地使用 React Router
- 🔙 从 v6 迁移 - 对
<BrowserRouter>的使用感到满意 - 🔄 已有数据层 - 你的数据层要么跳过待定状态(如本地优先、后台数据复制/同步),要么有自己的抽象来处理它们
- 📦 从 Create React App 迁移 - 虽然你可能想考虑框架模式
推荐:保持简单,使用声明式模式。
迁移路径
从声明式到数据模式
// 之前:声明式模式
<Route path="/user/:id" element={<UserProfile />} />
// 之后:数据模式
{
path: "/user/:id",
element: <UserProfile />,
loader: async ({ params }) => {
return fetchUser(params.id);
}
}
从数据模式到框架模式
需要安装 Vite 插件并重构项目结构:
npm install @react-router/dev
然后配置 vite.config.js 并按照文件系统路由约定组织代码。
总结
React Router 的三种模式各有千秋:
- 声明式模式适合追求简单和控制的开发者
- 数据模式适合需要数据功能但不想被框架束缚的项目
- 框架模式适合追求完整功能和开发效率的团队
选择哪种模式取决于你的项目需求、团队能力和长期规划。记住,没有一种模式是完美的,只有最适合你当前情况的模式。
如果你正在构建新项目,建议从声明式模式开始,随着需求增长逐步升级到数据模式或框架模式。如果你需要完整的全栈功能,直接选择框架模式可能是更好的选择。