← FC Coder · HomePhase 03 · Lesson 33 · 60 min
Lesson33
Phase Three · Fullstack · Matchday 33 · Read From Cabinet
Server 读 DB
0 个 API · 0 个 useState
Today's 3 Jobs · 今天这三件事
- 01app/players-db/page.tsx · async server · await db.select0 个 useState
- 02fantasy 拆 server + client boardserver 拿数据 · client 管 state
- 03🌟 验证 fantasy 还能用 · 但数据来自 DBJSON 退役
Server Component 直读 DB —— `await db.select().from(players)` 一行。fantasy 拆 server wrapper + client board:server 拿数据 · client 管状态。这是 Phase 3 关键 pattern · 反复见。
Concept · Server 拿 / Client 管
边界清晰 · 一个组件一种职责
// server
const rows = await db.select().from(players);
return <FantasyBoard initialPlayers={rows} />;
const rows = await db.select().from(players);
return <FantasyBoard initialPlayers={rows} />;
server
async function · await db.select()
client
use client · useState · onClick
桥
server 传 initialPlayers prop 给 client
一个组件不能 async + useState。所以拆。
Half 2 · 在屏幕上
server 读 · fantasy 拆 · AI elite
01Server 读 DB 渲染
01Min
Cursor + pnpm dev
老三件套。
02Min
app/players-db/page.tsx (server)
import { db } from '@/lib/db'; import { players } from '@/lib/schema'; export default async function() { const rows = await db.select().from(players); return ...players.map(p => <PlayerCard player={p} />); }
03Min
访问 /players-db · 看 22 张卡(数据来自 DB)
DevTools Network 看 0 个 /api 请求。HTML 直接带数据。
02Fantasy 拆 server + client
04Min
app/fantasy/page.tsx · 改 server async wrapper
不标 use client。export default async function FantasyPage() { const rows = await db.select().from(players); return <FantasyBoard initialPlayers={rows} />; }
05Min
app/fantasy/fantasy-board.tsx · 标 use client · 收 props
把原 fantasy 内容搬这里。'use client'。export function FantasyBoard({ initialPlayers }: { initialPlayers: Player[] }) { ...原 useState/useLocalStorage 逻辑 ... }
06Min
刷新 /fantasy 验证 · 22 张卡 + 阵容板 + 化学度都对
数据来自 DB 但交互不变。这是 Phase 3 关键 pattern。
03AI 写 /players-elite
07Min
AI Supervised:写 /players-elite (rating >= 85)
Cmd+K · 'server 页面 · .where(gte(players.rating, 85)) · .orderBy(desc(rating))'。爸爸陪坐。
08Min
📸 截图 /players-db + /players-elite
Phase 3 第八张战利品。
讲给爸爸听
4 题 · Server / Client 分工
01为什么 server 能直 await db?Hint ↓
服务器能直连 DB · 安全。client 直连 DB 会暴露密码 / secrets。
02server 拿数据 + client 管状态边界?Hint ↓
Server 一次拿初始数据 · client 之后用 useState 操作。一个组件不能同时 async + useState。
03DB vs JSON import?Hint ↓
DB 能改 / 查 / 多用户 / 持久 · JSON 死的。Phase 34 起会写 DB。
04await db.select() 返回什么类型?Hint ↓
TS 自动推 Player[] · Drizzle 知道 schema。第 20 课 interface 心法延续。
温度计
给「Server 读 DB」打分
今天难度Difficulty
0
今天开心Fun
0
Final Whistle · 终场哨
数据真从档案柜出来 —— fantasy 升级到 DB driven。
还有 8 步没打勾。Step 3 看到 22 张卡 + Step 6 fantasy 验证通过 = 算过。