React Router(Data 模式)从零接入指南:Vite 脚手架、安装与 RouterProvider 渲染
本指南讲解如何在浏览器端以 Data 模式接入 React Router:先用 Vite 等打包器引导一个 React 项目,通过 npm 安装 react-router 包,随后用 createBrowserRouter 创建数据路由并用 react-router/dom 导出的 RouterProvider 渲染到页面。读完本文你可以独立搭建出一个最小可运行、支持 loader/action 等数据能力的路由应用,并理解 Data Router 的初始化时机与选项含义。本文对应仓库文档 docs/start/data/installation.md,面向 Data 模式([MODES: data])。
一、Data 模式简介:一条独立的接入路径
React Router 提供多种「使用模式」,本文所属的 Data 模式(文档 front matter 中标有 [MODES: data],见 docs/start/data/index.md)对应的是数据路由 API:由 createBrowserRouter、createHashRouter、createMemoryRouter 等工厂函数创建路由器,再把路由器实例交给 <RouterProvider> 渲染。
与声明式 <BrowserRouter> + <Routes> 的接入方式不同,Data 模式把路由表(route objects)作为 createBrowserRouter 的第一个参数传入,路由对象可以承载 loader、action、middleware 等数据加载与变更逻辑。因此在动手之前,建议你先明确自己的模式选择:如果只需要纯前端路由匹配,可参考 docs/start/declarative/index.md;如果需要一个 HTTP 风格的数据层 + 客户端路由,则按本文的 Data 模式路径操作。
二、第一步:用打包器模板引导项目
你可以从 Vite 的 React 模板开始,在交互式提示中选择 "React",也可以用你习惯的其他方式(Parcel、Webpack 等)初始化应用,React Router 本身并不依赖某个特定打包器。
npx create-vite@latest
按提示选择框架 "React" 与语言变体(TypeScript / JavaScript)即可得到一个带 index.html、src/main.tsx、vite.config.ts 的最小工程。
仓库内自带的 Data 模式最小示例 playground/data 即采用这种结构,可当作权威参考:
- playground/data/index.html:HTML 入口,提供
<div id="root">挂载点; - playground/data/src/main.tsx:应用入口,完成路由创建与渲染;
- playground/data/vite.config.ts:Vite 配置;
- playground/data/package.json:依赖声明(
react、react-dom、react-router与@vitejs/plugin-react),并带有dev/build/preview三个脚本。
由于 react-router 包本身是纯 JavaScript 库,理论上用 Vite 之外的任意 bundler(如 Parcel、Webpack、esbuild 等)也可以正常接入,只是需要自行配置 JSX 与 TypeScript 编译环节。
三、第二步:从 npm 安装 React Router
在项目根目录执行:
npm i react-router
安装完成后,你的 package.json 中会出现 react-router 依赖。以仓库当前版本(见 packages/react-router/package.json)为参照,有几个信息值得注意:
-
入口结构:包的
exports字段区分了两个子路径(见 packages/react-router/package.json#L22-L48):react-router("."):核心导出,包括createBrowserRouter、createHashRouter、createMemoryRouter、各类 hooks 等;react-router/dom("./dom"):DOM 专属导出,最重要的是RouterProvider组件。
这正是下面代码中两条 import 分属两个模块路径的原因。
-
React 版本要求:
peerDependencies声明react >= 19.2.7、react-dom >= 19.2.7(react-dom 为可选 peer,见 packages/react-router/package.json#L102-L110)。请确保安装的 React 版本满足该要求。 -
Node 版本:包文件在
engines字段声明node >= 22.22.0(packages/react-router/package.json#L118-L120),本地开发环境需留意这一约束。
仓库内为
react-router包配置了 RSC(React Server Components)构建产物(子路径react-server)与开发/生产双构建,但 Data 模式的纯客户端使用无需关心这些细节,直接使用默认导出即可。
四、第三步:创建 Router 并通过 RouterProvider 渲染
安装完成后,在 src/main.tsx(或你定义的入口文件)中创建路由并挂载。下面是仓库文档 docs/start/data/installation.md 的完整示例:
import React from "react";
import ReactDOM from "react-dom/client";
import { createBrowserRouter } from "react-router";
import { RouterProvider } from "react-router/dom";
const router = createBrowserRouter([
{
path: "/",
element: <div>Hello World</div>,
},
]);
const root = document.getElementById("root");
ReactDOM.createRoot(root).render(
<RouterProvider router={router} />,
);
对应的 index.html 需要预留挂载点:
<!doctype html>
<html lang="en">
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
这段代码可以逐行拆解为三个动作:
- 从
react-router导入createBrowserRouter:传入路由对象数组作为第一个参数,得到 Data Router; - 从
react-router/dom导入RouterProvider:它是浏览器渲染 Data Router 的必备组件; - 用
react-dom/client的createRoot挂载:与普通 React 应用一致。
两种路由定义风格
上述示例用 element 属性直接给 JSX,而仓库里同时存在大量使用 Component 大写属性的写法(React 的 <Component/> 语法),例如 playground/data/src/main.tsx 中:
const router = createBrowserRouter([
{
id: "index",
path: "/",
loader() {
return { message: "Hello React Router!" };
},
Component() {
let data = useLoaderData();
return <h1>{data.message}</h1>;
},
},
]);
ReactClient.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>,
);
两者都是合法的。区别在于:element 接收一个创建好的 React 元素,每次路由状态变化该元素会作为 props 重建;Component 接收组件本身,更适合携带 loader/action 的数据型路由对象,也是 Data 模式更推荐的风格。更完整的路由对象字段(path、children、loader、action、handle 等)在 docs/start/data/route-object.md 中展开讲解。
RouterProvider 的实际实现
从源码看,react-router/dom 的 RouterProvider 是对核心组件的薄包装——它把 ReactDOM.flushSync 注入进去,保证 React Router 在需要同步刷新的场景(如部分导航状态更新)能正确工作,见 packages/react-router/lib/dom-export/dom-router-provider.tsx#L9-L11:
export function RouterProvider(props: RouterProviderProps) {
return <BaseRouterProvider flushSync={ReactDOM.flushSync} {...props} />;
}
而 createBrowserRouter 的内部实现则把路由表、选项与 DOM History 历史源组装成一个 Data Router 并立即初始化,见 packages/react-router/lib/dom/lib.tsx#L646-L664:
export function createBrowserRouter(
routes: RouteObject[],
opts?: DOMRouterOpts,
): DataRouter {
return createRouter({
basename: opts?.basename,
getContext: opts?.getContext,
future: opts?.future,
history: createBrowserHistory({ window: opts?.window }),
hydrationData: opts?.hydrationData || parseHydrationData(),
routes,
mapRouteProperties: defaultMapRouteProperties,
hydrationRouteProperties,
dataStrategy: opts?.dataStrategy,
patchRoutesOnNavigation: opts?.patchRoutesOnNavigation,
window: opts?.window,
instrumentations: opts?.instrumentations,
}).initialize();
}
从中可以看到两个关键点,可作为理解 Data Router 行为的事实依据:
- 它基于 History API:
createBrowserRouter使用createBrowserHistory,即通过history.pushState与history.replaceState管理 URL,见 packages/react-router/lib/dom/lib.tsx#L622-L625 的 JSDoc。因此应用必须运行在支持 History API 的浏览器环境中。 - 它会立即调用
initialize():router 创建后马上完成初始匹配与状态装配,返回的是已初始化的 router 实例,而非懒初始化对象。若无服务端水合数据,parseHydrationData()(packages/react-router/lib/dom/lib.tsx#L709-L718)会尝试读取window.__staticRouterHydrationData以实现 SSR 场景的数据接力。
五、重要原则:Data Router 不要放在 React State 中
安装文档用 <docs-info> 明确给出了官方告诫,这里原文转述并解释原因:
Data Routers should not be held in React state. You should create your router once outside of the React tree and pass it to
<RouterProvider>. You can usepatchRoutesOnNavigationto add additional routes programmatically.
即:不要在 React state 中持有 Data Router。应当只创建一次,在 React 树外部创建,然后把它传给 <RouterProvider>;需要动态追加路由时,使用 patchRoutesOnNavigation 以编程方式补入,而不是重新创建 router。
原因可以从实现层面推断:router 是有状态的单例对象(内部维护 state、navigation 队列、数据加载流程等)。若把 createBrowserRouter 放进组件体内的 useState/useMemo 或每次渲染都重新创建:
- 每个渲染周期都会生成一个新的 router 并触发一次
initialize(),导航状态、加载器缓存随之丢失,甚至可能引发多次订阅与内存泄漏; <RouterProvider>以router为唯一订阅源,router 实例频繁更换意味着整个渲染树反复重建。
因此,仓库中所有真实示例都遵循「模块顶层创建一次」的写法——例如 playground/data/src/main.tsx#L6-L18 在 main.tsx 模块作用域直接调用 createBrowserRouter,随后在 render 中引用同一个 router 变量。若在组件内需要按条件展示路由,应通过 <RouterProvider> 配合 router.state 或数据路由的声明式能力来处理,而不是替换 router 实例。
动态补路由:patchRoutesOnNavigation
如果在启动时无法预知完整路由树(例如按需加载远程路由配置、做代码分割),不必重建 router。createBrowserRouter 的选项 patchRoutesOnNavigation 允许在导航匹配发生缺口时以编程方式 patch 新路由。官方 JSDoc 给出的最小模式是(见 packages/react-router/lib/dom/lib.tsx#L400-L418):
const router = createBrowserRouter([{ id: "root", path: "/", Component: Root }], {
async patchRoutesOnNavigation({ patch, path }) {
if (path === "/root-sibling") {
let route = await getRootSiblingRoute();
patch(null, [route]); // 作为根路由的兄弟补入
}
},
});
更常见的做法是与动态 import() 结合,把整棵子路由树异步载入(同一段 JSDoc 中给出了 patch(null, await import("./dashboard")) 的完整示例)。由此,Data Router 既能保证路由树的「只创建一次」,又不必牺牲按需加载能力。
六、createBrowserRouter 可选参数(DOMRouterOpts)
createBrowserRouter(routes, opts) 的第二个参数是 DOMRouterOpts,定义在 packages/react-router/lib/dom/lib.tsx#L136-L620。主要选项如下表所示:
| 选项 | 类型 | 作用 |
|---|---|---|
basename |
string |
应用部署的基础路径前缀,例如部署在 /app 下时所有路由以此为前缀 |
window |
Window |
覆盖全局 window 对象,便于测试或非标准宿主环境(默认取全局 window) |
hydrationData |
HydrationState |
SSR 场景下手动传入服务端水合数据(loaderData/actionData/errors),支持只水合部分路由的「部分水合」(Partial Hydration) |
future |
Partial<FutureConfig> |
开启 Router 的未来兼容性 flags |
dataStrategy |
DataStrategyFunction |
覆盖默认的「loaders 并行执行」数据策略,自定义 loader/action 的编排方式 |
patchRoutesOnNavigation |
Function |
导航匹配时按需补入路由(见上一节) |
getContext |
() => RouterContextProvider |
为每次导航/fetcher 调用生成全新的路由上下文实例,供 loader/action/middleware 的 context 参数使用 |
instrumentations |
ClientInstrumentation[] |
插桩观测点,可包装 navigation、fetch、loader、action、middleware 以做日志与性能追踪 |
其中 hydrationData 与 SSR 的配合细节、dataStrategy 的自定义加载策略,分别对应框架模式与 docs/explanation/backend-for-frontend.md 等专题文档。对纯客户端 SPA 而言,大多数场景只需 basename 与可选的 future 即可。
七、配套的 Router 创建函数与进阶路径
Data 模式的 Router 创建函数不止 createBrowserRouter 一个,文档中以 [MODES: data] 组织的系列还包括:
createMemoryRouter:不依赖浏览器 URL,适用于测试环境与 React Native 等非 DOM 宿主,常与createMemoryHistory场景对应;createHashRouter:通过 URL hash 段管理路径,见 packages/react-router/lib/dom/lib.tsx#L689-L707,实现上与createBrowserRouter几乎一致,只是 history 换成createHashHistory;createStaticHandler/createStaticRouter:服务端渲染配套,输出静态上下文,用于 SSR 环境。
它们的详细文档集中在 docs/api/data-routers/createBrowserRouter.md 等 docs/api/data-routers 目录下。
八、安装后的下一步
Router 挂载完成只是起点。仓库 docs/start/data 目录按学习顺序组织,建议按下面的顺序继续:
- Routing(路由配置):学习路由对象数组如何组织,覆盖嵌套路由(
children)、布局路由、索引路由(index: true)、路径前缀组、动态段(:param)、可选段与 splat(*)等全部路径语法; - Route Object(路由对象):掌握
loader/action/handle/HydrateFallback等字段的完整语义; - Data Loading(数据加载):用
useLoaderData消费 loader 返回数据,理解加载与渲染的生命周期; - Actions(数据变更):处理表单提交与数据写入;
- Navigating(导航) 与 Pending UI(过渡期 UI):掌握
Link、useNavigate及 pending/optimistic UI 的开发方式。
另外,仓库中的 playground/data 与 playground/framework 等 playground 工程是可直接运行验证的参考实现——在仓库根目录安装依赖后执行 pnpm dev(以各自 package.json 的 scripts 为准)即可看到 Data 模式的真实运行效果。若你需要一套开箱即用的完整工程,也可以关注仓库中 integration/helpers 下的各种 Vite 模板(如 integration/helpers/vite-7-template),它们展示了 Data 模式在不同 Vite 大版本下的标准目录与配置形态。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00