首页
/ React Router(Data 模式)从零接入指南:Vite 脚手架、安装与 RouterProvider 渲染

React Router(Data 模式)从零接入指南:Vite 脚手架、安装与 RouterProvider 渲染

2026-09-07 22:26:00作者:魏侃纯Zoe

本指南讲解如何在浏览器端以 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:由 createBrowserRoutercreateHashRoutercreateMemoryRouter 等工厂函数创建路由器,再把路由器实例交给 <RouterProvider> 渲染。

与声明式 <BrowserRouter> + <Routes> 的接入方式不同,Data 模式把路由表(route objects)作为 createBrowserRouter 的第一个参数传入,路由对象可以承载 loaderactionmiddleware 等数据加载与变更逻辑。因此在动手之前,建议你先明确自己的模式选择:如果只需要纯前端路由匹配,可参考 docs/start/declarative/index.md;如果需要一个 HTTP 风格的数据层 + 客户端路由,则按本文的 Data 模式路径操作。

二、第一步:用打包器模板引导项目

你可以从 Vite 的 React 模板开始,在交互式提示中选择 "React",也可以用你习惯的其他方式(Parcel、Webpack 等)初始化应用,React Router 本身并不依赖某个特定打包器。

npx create-vite@latest

按提示选择框架 "React" 与语言变体(TypeScript / JavaScript)即可得到一个带 index.htmlsrc/main.tsxvite.config.ts 的最小工程。

仓库内自带的 Data 模式最小示例 playground/data 即采用这种结构,可当作权威参考:

由于 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)为参照,有几个信息值得注意:

  1. 入口结构:包的 exports 字段区分了两个子路径(见 packages/react-router/package.json#L22-L48):

    • react-router"."):核心导出,包括 createBrowserRoutercreateHashRoutercreateMemoryRouter、各类 hooks 等;
    • react-router/dom"./dom"):DOM 专属导出,最重要的是 RouterProvider 组件。

    这正是下面代码中两条 import 分属两个模块路径的原因。

  2. React 版本要求peerDependencies 声明 react >= 19.2.7react-dom >= 19.2.7(react-dom 为可选 peer,见 packages/react-router/package.json#L102-L110)。请确保安装的 React 版本满足该要求。

  3. Node 版本:包文件在 engines 字段声明 node >= 22.22.0packages/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>

这段代码可以逐行拆解为三个动作:

  1. react-router 导入 createBrowserRouter:传入路由对象数组作为第一个参数,得到 Data Router;
  2. react-router/dom 导入 RouterProvider:它是浏览器渲染 Data Router 的必备组件;
  3. react-dom/clientcreateRoot 挂载:与普通 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 模式更推荐的风格。更完整的路由对象字段(pathchildrenloaderactionhandle 等)在 docs/start/data/route-object.md 中展开讲解。

RouterProvider 的实际实现

从源码看,react-router/domRouterProvider 是对核心组件的薄包装——它把 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 APIcreateBrowserRouter 使用 createBrowserHistory,即通过 history.pushStatehistory.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 use patchRoutesOnNavigation to 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-L18main.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.mddocs/api/data-routers 目录下。

八、安装后的下一步

Router 挂载完成只是起点。仓库 docs/start/data 目录按学习顺序组织,建议按下面的顺序继续:

  1. Routing(路由配置):学习路由对象数组如何组织,覆盖嵌套路由(children)、布局路由、索引路由(index: true)、路径前缀组、动态段(:param)、可选段与 splat(*)等全部路径语法;
  2. Route Object(路由对象):掌握 loader/action/handle/HydrateFallback 等字段的完整语义;
  3. Data Loading(数据加载):用 useLoaderData 消费 loader 返回数据,理解加载与渲染的生命周期;
  4. Actions(数据变更):处理表单提交与数据写入;
  5. Navigating(导航)Pending UI(过渡期 UI):掌握 LinkuseNavigate 及 pending/optimistic UI 的开发方式。

另外,仓库中的 playground/dataplayground/framework 等 playground 工程是可直接运行验证的参考实现——在仓库根目录安装依赖后执行 pnpm dev(以各自 package.json 的 scripts 为准)即可看到 Data 模式的真实运行效果。若你需要一套开箱即用的完整工程,也可以关注仓库中 integration/helpers 下的各种 Vite 模板(如 integration/helpers/vite-7-template),它们展示了 Data 模式在不同 Vite 大版本下的标准目录与配置形态。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388