菜单

水合与数据加载

相关源文件

本文档介绍了 React Router 中的 hydration(数据同步)过程和数据加载策略,重点关注服务器端渲染 (SSR) 和客户端执行之间的交互。有关创建 loader 和 action 的信息,请参阅 Data APIs,有关通用 SSR 架构,请参阅 Server-Side Rendering

Hydration 概述

Hydration 是客户端 React 应用程序“接管”服务器端工作过程,将事件处理器附加到服务器渲染的 HTML 并恢复应用程序状态。在 React Router 中,这涉及到在服务器和客户端之间仔细传递路由匹配数据、loader 数据和错误状态。

来源:packages/react-router/lib/dom-export/hydrated-router.tsx79-210 packages/react-router/lib/server-runtime/server.ts416-512

Single Fetch 数据策略

React Router v7 实现了一种“Single Fetch”(单一请求)数据加载策略,它会整合数据请求以优化性能。它不像每个路由的 loader 都单独发起网络请求,而是将它们批量处理成一个请求。

Single Fetch 工作原理

  1. 在服务器端渲染和客户端导航时,React Router 都会收集所有需要调用的路由 loader。
  2. 在服务器上,这些 loader 会直接执行。
  3. 在客户端,路由器会发出一个包含所有 loader 请求的单一 .data 请求。
  4. 数据通过由 turbo-stream 启用的流式格式传输。

来源:packages/react-router/lib/dom/ssr/single-fetch.tsx156-333 packages/react-router/lib/server-runtime/single-fetch.ts45-77

Hydration 数据流

来源:packages/react-router-dev/vite/plugin.ts388-417 packages/react-router/lib/server-runtime/server.ts228-273

Hydration 状态管理

Hydration 状态包含从服务器序列化到客户端的 loader 数据、action 数据和错误。 HydratedRouter 组件负责引导此过程。

在 hydration 过程中

  1. 服务器执行路由,并将数据收集到 StaticHandlerContext
  2. 这些数据被序列化并嵌入到 HTML 响应中
  3. 在客户端,HydratedRouter 组件
    • window.__reactRouterContext 解码 hydration 状态
    • 使用此初始状态创建路由器
    • 管理具有客户端 loader 的路由的局部 hydration

来源:packages/react-router/lib/router/router.ts286-356 packages/react-router/lib/dom-export/hydrated-router.tsx128-196

局部 Hydration (Partial Hydration)

React Router 支持局部 hydration,这是一种技术,其中一些路由立即使用服务器数据,而另一些在 hydration 过程中获取新数据。

HydrateFallback 组件

对于具有客户端 loader 的路由,React Router 提供了一个 HydrateFallback 组件,该组件在 hydration 期间渲染,直到客户端数据可用为止。

在 hydration 期间,React Router 检测到以下路由:

  1. 具有 clientLoader 函数
  2. 提供 HydrateFallback 组件
  3. 在 hydration 状态中没有服务器数据

对于这些路由,HydrateFallback 组件会在初始 hydration 期间渲染,并且客户端 loader 会被执行以获取新数据。

来源:packages/react-router/lib/dom-export/hydrated-router.tsx145-187 packages/react-router/lib/dom/ssr/routes.tsx65-117

导航过程中的数据流

一旦 hydration 完成,React Router 会使用 Single Fetch 策略或传统的客户端数据加载来处理导航。

路由 Loader 类型

React Router 区分不同类型的 loaders

Loader 类型执行用于Hydration 行为
loader服务器端服务器渲染数据从服务器状态 hydration
clientLoader客户端仅客户端数据在 hydration 期间/之后运行
两者Server + Client (服务器 + 客户端)混合方法服务器数据最初可用,在客户端刷新

来源:packages/react-router/lib/dom/ssr/single-fetch.tsx285-320 packages/react-router/lib/dom-export/hydrated-router.tsx149-194

战争迷雾与惰性加载

React Router 采用“Fog of War”(战争迷雾)的路由发现方式,即路由在用户通过应用程序进行导航时按需加载。

这种方法

  1. 减少初始 JavaScript 有效负载
  2. 提高首次可交互时间 (Time-to-Interactive)
  3. 优化资源加载

路由器维护一个已发现路由的缓存,并在导航过程中根据需要获取新路由。

来源:packages/react-router/lib/dom/ssr/fog-of-war.ts29-97 packages/react-router-dev/vite/plugin.ts326-384

Hydration 实现细节

服务器端 (Server-Side)

在服务器上,React Router

  1. 匹配请求 URL 与路由
  2. 执行路由 loader 和 action
  3. StaticHandlerContext 中捕获数据和错误
  4. 序列化这些数据以供客户端 hydration
  5. 对于 Single Fetch,使用 turbo-stream 对数据进行编码

来源:packages/react-router/lib/server-runtime/server.ts387-512 packages/react-router/lib/router/router.ts392-404

客户端 (Client-Side)

在客户端,React Router

  1. window.__reactRouterContext 解码 hydration 状态
  2. 使用来自服务器的初始数据创建路由
  3. 选择性地运行客户端 loader
  4. 建立用于延迟加载数据的流式连接

HydratedRouter 组件是此过程的主要入口点,它使用 hydration 状态设置路由器并管理从服务器到客户端渲染的过渡。

来源:packages/react-router/lib/dom-export/hydrated-router.tsx77-210 packages/react-router/lib/dom/global.ts1-17

SPA 模式和预渲染

React Router 还支持 SPA 模式,并可选预渲染,采用混合方法

  1. 路由可以在构建时进行预渲染(prerender: true
  2. 未预渲染的路由会回退到客户端渲染
  3. 服务器为预渲染的路由提供静态 HTML
  4. 在 SPA 模式下,客户端仅 hydration 根路由数据

来源: integration/vite-spa-mode-test.ts21-150 integration/vite-prerender-test.ts156-287

处理水合时的错误

在水合过程中处理错误需要特别考虑

  1. 服务器端错误会被序列化并包含在水合状态中
  2. 客户端在水合过程中发生的错误会被最近的错误边界捕获
  3. 客户端加载器中的错误与水合错误分开处理

该系统确保错误不会中断水合过程,并且会激活适当的错误边界。

来源: packages/react-router/lib/server-runtime/server.ts442-466 packages/react-router/lib/dom-export/hydrated-router.tsx190-195

结论

React Router 的水合(hydration)和数据加载架构,通过单一获取策略、局部水合以及“战争迷雾”(Fog of War)方法协同工作,实现了从服务器渲染内容到交互式客户端应用程序的平滑过渡,优化了性能,同时保持了开发者的易用性。