← [ BACK_TO_NOTES ]

Next.js Server Component 数据获取

Next.js App Router 的 Server Component 可以直接 await 数据源,在服务器读取 DB、内部服务或第三方 API,并把 UI-safe 结果传给页面与客户端组件。

#concept #growing #frame #react

[!info] related notes

  • 所属 MOC:
  • 前置概念:
  • 并列概念:
  • 易混淆概念:
  • 关系笔记:

Next.js Server Component 数据获取

一句话定义

在 Next.js App Router 中,Server Component 可以在服务端直接执行异步数据获取:

export default async function Page() {
  const data = await getData()
  return <View data={data} />
}

它不需要为了“页面首次拿数据”而先在浏览器 mount,再通过 useEffect + useState 发起第一次请求。

Server Component 可以从哪里拿数据

Server Component
      │
      ├─ internal data layer / DB
      ├─ owning service API
      ├─ third-party API
      └─ server-only filesystem / secret-backed resource

数据源由架构 ownership 决定,而不是由组件语法决定。

当前 Next.js 应用自己拥有数据

Server Component
→ data/repository
→ ORM / SQL
→ DB

独立后端服务拥有数据

Server Component
→ Go / Java / Python API
→ Domain Service
→ DB

选择 DB 还是 API 继续看 数据所有权与服务边界。

为什么不默认 useEffect(fetch) 获取首屏数据

传统 Client Component:

"use client"

export function GoalsPage() {
  const [goals, setGoals] = useState([])

  useEffect(() => {
    fetch('/api/goals')
      .then(r => r.json())
      .then(setGoals)
  }, [])

  return <GoalList goals={goals} />
}

首次链路通常需要:

HTML / JS
  ↓
download JS
  ↓
hydrate
  ↓
useEffect
  ↓
HTTP request
  ↓
setState
  ↓
render data

如果数据本来就是首屏必需,可以改成:

request
  ↓
Server Component awaits data
  ↓
server render / RSC result
  ↓
browser receives useful content

因此,useEffect 仍然是有价值的副作用工具,但不应被当作 Next.js 首屏 Server Data 的默认获取层。相关背景见 React SSR 数据获取位置。

直接 DB 的真正含义

Next.js 官方文档常展示 Server Component 直接查询数据库。这成立的前提是:当前应用有资格拥有这层数据访问。

可以安全执行 DB client
≠
应该绕过所有后端服务

如果 Go Backend 才是某领域的 owner,那么 Server Component 应调用 Go API,而不是直接连接它的数据库。

Server Component 与 Secret

Server Component 可以使用 server-only secret:

DATABASE_URL
service credential
third-party API secret

因为这些实现不会像 Client Component bundle 一样发送到浏览器。

但要区分:

Secret 不自动泄露
≠
查询结果自动安全

例如:

const user = await db.user.findUnique(...)

如果把完整实体直接传给 Client Component,其中的 passwordHash、内部 flags 或 private token 仍然可能被序列化出去。

因此应该建立 UI-safe projection:

DB / service entity
       ↓
select / mapper / DTO
       ↓
UI-safe data
       ↓
Server / Client Component

Server 获取数据,Client 负责交互

Client Component 不代表它必须自己 fetch 初始数据。

export default async function ProfilePage() {
  const profile = await getProfile()
  return <ProfileEditor initialProfile={profile} />
}
"use client"

export function ProfileEditor({ initialProfile }) {
  const [name, setName] = useState(initialProfile.name)
  // browser interaction
}

边界:

Initial data fetch
→ Server

Browser interaction
→ Client

这是一种非常常见的 App Router 组合方式。

与 TanStack Query 的关系

Server Component 更自然地解决:

页面进入时需要的 server data

TanStack Query 更自然地解决:

页面已经运行后,客户端如何缓存、refetch、retry、invalidate、optimistic update

因此二者不是互斥替代:

Server-rendered initial data
+
Client-managed server-state lifecycle

具体 client cache 语义见 TanStack Query 服务端状态管理。

BodySense 例子

Assessment Result

GET /assessment/123
      ↓
Next.js Server Component
      ↓
BodySense Go API
      ↓
Assessment domain logic
      ↓
UI-safe result

不应因为 Server Component 能直接访问 DB,就绕过 Go 的业务边界。

Consultation 初始历史

Page request
→ Server Component
→ Go API
→ existing conversation history
→ initial page

实时新事件则属于浏览器运行期:

Client Component
→ SSE
→ incremental runtime events

常见误区

“Client Component 必须自己 fetch”

错。Client Component 可以接收 Server Component 已经准备好的 props。

“Server Component 可以查 DB,所以 API 没用了”

错。独立服务 API 仍然承担 service ownership、权限与领域规则边界。

“只要在 Server Component 里 fetch,就一定更快”

不一定。真实性能还取决于请求并行、缓存、下游延迟和 Streaming。见 Request Waterfall 与并行数据获取。

最短记忆

首屏 server data
→ 优先 Server Component

数据源选择
→ 看 ownership

Client Component
→ 不等于必须 client fetch
创建于 2026/9/15 更新于 2026/9/16
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本