Server Components

2026年9月6日

page.tsxlayout.tsx 默认都是服务端组件。它们在 Node 里执行,可以 fs、连数据库、读只有服务器才有的密钥。

import { readFile } from "node:fs/promises";

export default async function Page() {
  const raw = await readFile("data.json", "utf8");
  const data = JSON.parse(raw) as { name: string };

  return <p>{data.name}</p>;
}
tsx

console.log 出现在终端。开发模式有时也会转发到浏览器控制台方便对,生产环境不要依赖这一点。


优点

  • 秘密不过桥:API Key、数据库 URL 只在服务器出现
  • 更瘦的 JS:这些模块不进客户端包
  • 能碰系统:文件、SQL、内部 RPC 都可以
  • 流式 HTML:配合 Suspense,算完一块推一块,FCP 更好

把取数写在组件里,少一层「专门为页面开的 BFF」,小项目很爽。连接池、事务、迁移该有的还是要有,别把 demo 查询直接当生产架构。


限制

服务端没有 DOM / BOM。useStateuseEffectonClickwindowdocument 都不能用。

JavaScript 平时说的三块:

部分服务端
ECMAScript(语法、Array、Promise)能用
DOM不能
BOM(windowlocation不能

下面这种会直接报错:

import { useState } from "react";

export default function Page() {
  const [n, setN] = useState(0);
  return <button onClick={() => setN(n + 1)}>{n}</button>;
}
tsx

有交互,拆一个客户端子组件。数据仍由服务端取,当作 props 传下去。


取数怎么写

export default async function Page() {
  const res = await fetch("https://api.example.com/posts", {
    next: { revalidate: 60 },
  });
  const posts = await res.json();
  return (
    <ul>
      {posts.map((p: { id: string; title: string }) => (
        <li key={p.id}>{p.title}</li>
      ))}
    </ul>
  );
}
tsx

缓存、强制动态、connection() 这些放到后面「缓存策略」篇。这里先记住:服务端组件可以是 async 的,客户端组件不行。

下一篇:'use client' 之后发生了什么,以及为什么客户端不能再 import 服务端模块。


参考文档