静态即服务器
May 8, 2025
RSC 代表 React 服务器组件。
然而,尽管这个博客是用 RSC 构建的,它实际上是通过 Cloudflare CDN 的免费静态托管计划以静态方式进行服务的。它的花费恰好是零。
零。
这怎么可能?
这些不都是 React 服务器组件吗?
过去,“服务器”和“静态”框架被认为是两种截然不同的工具。例如,你可能会用 Rails 或 PHP 来做“服务器”应用,但如果你想生成一个“静态”的 HTML+CSS+JS 网站,你可能会用 Jekyll 或 Hugo。
不过,现在越来越多的框架开始同时支持“服务器”和“静态”输出模式。这基于一个事后看来很显然的洞见:你可以用任何“服务器”框架,通过在构建时运行它的“服务器”,然后对你想生成的每个页面发起请求,再把响应内容存储到磁盘上,从而得到一个“静态”网站。手动做这件事会很麻烦,所以新一代框架往往直接内置了这个能力。
我把这些框架称为“混合型”框架。它们在概念上是遵循请求/响应模型的“服务器”框架,但同时也支持“静态”输出选项。
有时候,专注于某个用例并做到极致确实值得,但我认为这里并不是这种情况。我没发现“静态”工具仅仅因为是“静态”而能为开发者或终端用户带来什么额外价值。这并不意味着“纯静态”工具不好,但我看不出有什么理由偏爱它们。相反,我能看到多个选择“混合型”框架的实际理由。
首先,“混合型”方案减少了工具的割裂——既然两者的重叠如此之大,为什么要维护两个生态?它们的区别其实只是代码何时运行。
“混合型”方案还带来了更高的灵活性和更细粒度的控制。它不会把你锁定在某一种方式里。实际上,现在你可以按路由决定是用“服务器”还是“静态”渲染。你可以先做一个完全“静态”的网站,后续再加一个“服务器”页面来展示动态内容;也可以先做“服务器”站点,再加一些“静态”的营销页面。你的项目中,有的完全“静态”,有的用“服务器”,但它们可以共享代码和插件。而且,从某种意义上说,请求/响应的思维模型本身就很自然、直观。
这种方式并不局限于 RSC。例如,Astro 就不是一个 RSC 框架,但它是一个“混合型”框架。它默认生成“静态”网站,但你可以选择开启诸如 API 路由 和 按需渲染 这样的“服务器”特性。
当然,RSC 也同样适用。
我的博客是用 Next.js 构建的,它默认输出“静态”网站。实际上,我还通过 output: "export"
选项 强制开启了这一点,这会禁用所有需要“服务器”的功能——而我并没有服务器。如果我尝试用任何“服务器”特性,静态构建就会失败,这正是我想要的效果。
换句话说,这个 React 服务器组件 会在部署时的构建阶段运行:
export default async function Home() {
const posts = await getPosts();
return (
<div className="relative -top-[10px] flex flex-col gap-8">
{posts.map((post) => (
<Link
key={post.slug}
className="block py-4 hover:scale-[1.005] will-change-transform"
href={"/" + post.slug + "/"}
>
<article>
<PostTitle post={post} />
<PostMeta post={post} />
<PostSubtitle post={post} />
</article>
</Link>
))}
</div>
);
}
你可以在我的主页看到它的输出。await getPosts()
这个调用是从文件系统读取内容——它既不是客户端请求,也不是某种服务器运行时代码。这个服务器组件是在我的静态博客部署时构建阶段运行的。
是的,我们说“React 服务器组件”,即便它们是“静态”运行的,这确实有点让人困惑。但我已经解释过,任何“服务器”框架本质上都可以是“静态”框架。你只需要提前运行它,把响应保存到磁盘上即可。
所以,让我们一起放下这个纠结吧。
我们写的代码其实完全一样。
“静态”,就是提前运行的“服务器”。
Pay what you like