众所周知,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