众所周知,React Router 原本是为 客户端渲染(CSR) 和 客户端导航 设计的,它依赖 useLocation、useNavigate、useParams 等 Hook 来获取/改变路由状态。同时,服务端组件 不能访问 window、history、location 等 浏览器 API。
这就导致一直以来 React Router 无法被 SRC 时使用,但是这种情况从今天开始发生了改变。
本文将介绍 React Router 如何通过 Framework Mode 和 RSC 支持解决这些问题,以及 Framework Mode 的底层原理。
React 的三大挑战在服务器端渲染 React 应用时,开发者面临以下三个主要挑战:
Inlining Data):如何高效地将服务器端的数据传递到浏览器中的 React 组件,同时保持性能和开发效率。Streaming UI):如何确保用户在所有数据加载完成之前就能看到部分 UI,从而提升用户体验。Code Splitting Routes):如何避免在首次页面加载时下载整个应用的代码,以减少加载时间。这些挑战直接影响了应用的性能、用户体验和开发复杂度。React Router 通过其 Framework Mode 和 RSC 支持提供了灵活的解决方案,下面我们逐一分析。
挑战:在服务器端渲染中,数据需要从服务器高效传递到浏览器中的 React 组件。如果处理不当,可能导致性能瓶颈或复杂的代码逻辑。
解决方案:
React Router 通过 loader 函数实现数据内联。开发者可以在路由中定义 loader 函数,这些函数在服务器端执行并返回数据。React Router 随后通过 useLoaderData() 钩子将数据传递给对应的组件。例如:export async function loader() {
return fetchDataFromServer();
}
function Component() {
const data = useLoaderData();
return <div>{data.title}</div>;
}
这种方式简化了数据管理,开发者无需手动处理数据传递的复杂逻辑。
RSC 通过从 服务器组件 直接向 "use client" 组件传递 props 来实现数据内联。服务器组件 在服务器端执行逻辑,将数据作为 props 传递给 客户端组件。这种方法减少了客户端的 JavaScript 负担,提高了性能。React Router 支持两种数据内联方式,开发者可以根据项目需求选择 Framework Mode 的 loader 函数或 RSC 的 props 传递方式。这种灵活性确保了新老项目都能无缝集成。挑战:在服务器端渲染中,如果用户需要等待所有数据加载完成才能看到页面,可能会导致较差的用户体验,例如长时间的空白页面或加载指示器。
解决方案:
React Router 通过 loader 函数返回 promises,结合 React 的 <Suspense> 和 <Await> 组件实现 UI 流式传输。开发者可以为 <Suspense> 指定 fallback 内容,在数据加载期间显示占位 UI。例如:function Component() {
const data = useLoaderData();
return (
<Suspense fallback={<div>加载中...</div>}>
<Await resolve={data.promise}>
{(resolvedData) => <div>{resolvedData.title}</div>}
</Await>
</Suspense>
);
}
这种方式让用户在数据加载期间看到部分 UI,提升了交互体验。
RSC 通过将 promises 作为 props 传递,在 服务器端 使用 await 处理异步数据,在 客户端 使用 use(promise) 钩子。这种方法同样支持 UI 的逐步渲染,减少了用户的等待时间。React Router 支持两种流式传输模式,开发者可以根据项目需求选择 Framework Mode 的 Suspense 机制或 RSC 的异步处理方式。这种兼容性为开发者提供了更大的灵活性。挑战:在传统的单页面应用(SPA)中,首次加载时浏览器可能需要下载整个应用的代码,这对于大型应用来说会导致较长的加载时间。
解决方案:
Framework Mode**:通过 routes.ts 配置文件,结合捆绑器插件(如 Vite),React Router 生成一个清单(manifest),将路由映射到对应的代码块(chunks)。浏览器只需下载当前路由所需的代码块。例如:// routes.ts
export default [
{ path: "/", component: "./Home.tsx" },
{ path: "/about", component: "./About.tsx" },
];
捆绑器插件会根据此配置生成代码块,确保高效加载。
React RSC**:RSC 通过在 服务器端 使用动态导入(dynamic imports)和 "use client" 指令,确保浏览器只下载当前路由所需的代码。这种方法无需额外的配置文件,直接在应用代码中定义路由。React Router RSC Support**:React Router 提供了一种更简单的架构,路由直接在应用代码中定义,无需 routes.ts 或捆绑器插件。这种方式减少了配置复杂性,使开发者更容易管理和扩展应用。Framework Mode 的底层原理Framework Mode 是 React Router 的一种高级模式,通过高度抽象化简化了 服务器端渲染 和 数据加载 的复杂性。其底层原理包括以下几个关键部分:
机制 | 描述 |
|---|---|
路由配置 | 通过 routes.ts 文件定义路由结构,捆绑器插件(如 Vite)处理该文件,生成路由到代码块的映射。 |
数据加载 | 每个路由可定义 loader 函数,在 服务器端 执行并返回数据,React Router 自动将数据传递给组件。 |
代码分割 | 捆绑器插件根据 routes.ts 生成清单(manifest),指导浏览器加载当前路由所需的代码块。 |
流式传输 | 支持 loader 函数返回 promises,结合 React 的 Suspense 机制,实现 UI 的流式传输,开发者可定义 fallback 内容。 |
这些机制的核心在于将复杂的 服务器端渲染 过程封装在 React Router 和捆绑器插件中。开发者只需定义路由和 loader 函数,React Router 负责处理数据传递、代码分割和 UI 渲染的细节。例如,捆绑器插件会自动生成 清单文件,确保浏览器只加载必要的代码块,而 Suspense 机制则通过 fallback 内容优化用户体验。
这种抽象让开发者可以专注于业务逻辑,而无需深入理解底层的 服务器端渲染 和数据流机制。Framework Mode 的强大之处在于它与捆绑器(如 Vite)的紧密集成,处理了代码分割和清单生成的复杂细节,使得开发过程更加顺畅。
React Router 与 RSC 的结合标志着 Web 开发的一个重要里程碑。通过解决 数据内联、UI流式传输 和 路由代码分割 三大挑战,React Router 为开发者提供了灵活且高效的工具!
今天的分享就这些了,感谢大家的阅读,如果文章中存在错误的地方欢迎指正!
参考官方博客原文:https://remix.run/blog/react-router-and-react-server-components#under-the-hood-how-framework-mode-works