React / Next.js
从 React Fiber、并发渲染到 Server Components,理解现代 React 全栈架构
现代 React 已经不再只是:
组件 + State + Props它逐渐演变成:
React
├── Component Model
├── Fiber
├── Scheduler
├── Concurrent Rendering
├── Server Components
├── Server Functions
└── Streaming而 Next.js 则进一步把:
React + Server + Router + Cache + Bundler + Deployment组合成一个完整应用运行时。
1. React 为什么需要 Fiber?
早期 React 的更新过程可以理解为:
更新状态
↓
计算组件树
↓
生成 DOM
↓
提交问题在于:
如果一次更新需要处理非常大的组件树,那么 JavaScript 执行可能长时间占用主线程。
浏览器主线程同时还负责:
- JavaScript
- Style
- Layout
- Paint
- Input
因此长时间 React 工作可能影响交互。
2. Fiber 的核心思想
Fiber 可以理解成:
把 React 工作拆成可以被调度的任务单元。
传统思路:
Render A
↓
Render B
↓
Render C
↓
Render DFiber:
Fiber A
↓
Fiber B
↓
Fiber C
↓
可暂停
↓
继续于是 React 可以根据优先级决定:
用户输入 > 动画 > 普通更新 > 低优先级更新3. Render 和 Commit
React 更新大体可以理解为:
Update
↓
Render Phase
↓
Commit PhaseRender Phase:
计算“应该是什么”。
Commit Phase:
真正修改 DOM。
所以:
function Component() {
console.log('render')
return <div />
}执行 render 不代表 DOM 已经更新。
4. 为什么 React Render 可以执行多次?
因为 Render Phase 可以被:
中断
重新执行
丢弃
重新计算因此 React Component 必须尽可能保持:
Render = Pure不要在 render 中执行:
fetch()
localStorage.setItem(...)
document.body.append(...)否则并发渲染下很容易出现问题。
5. useEffect 到底是什么?
很多人把:
useEffect(...)理解成:
“组件生命周期。”
这种理解已经比较过时。
更准确地说:
useEffect用来同步 React 状态与 React 外部系统。
例如:
React
↕
WebSocket或者:
React
↕
Browser API如果某件事情完全可以在 render 中计算:
const fullName = `${firstName} ${lastName}`就没有必要:
useEffect(() => {
setFullName(...)
}, [firstName, lastName])6. Server Components 改变了什么?
传统 SSR:
Server
↓
生成 HTML
↓
Browser
↓
HydrationServer Components 则进一步把:
Component本身分成:
Server Component
Client ComponentServer Component:
Server
↓
执行
↓
结果发送给客户端Client Component:
Browser
↓
JavaScript
↓
Interactive7. 为什么 Server Component 可以减少 JavaScript?
例如:
async function ProductPage() {
const product = await db.product.find()
return (
<ProductView product={product} />
)
}如果整个组件只负责:
查询数据 + 展示内容那么没有必要把数据库查询逻辑和大量展示逻辑发送到浏览器。
Server Components 的重要价值之一就是:
把不需要客户端交互的工作留在服务器。
8. "use client" 的真正含义
很多人认为:
'use client'等于:
“这个组件在浏览器执行。”
更准确的是:
它声明了一个 Client Component 边界。
一旦某个模块进入 Client Component 子树,它的依赖关系也会影响客户端 bundle。
因此:
'use client'
import hugeLibrary from 'huge-library'可能导致:
huge-library
↓
client bundle所以 "use client" 应该尽可能放在:
真正需要交互的叶子节点附近。
9. Server Component 和 Client Component 怎么划分?
推荐:
Page
├── Server
│ ├── Data
│ ├── SEO
│ └── Layout
│
└── Client
├── Interaction
├── State
└── Browser API而不是:
Page
↓
'use client'
↓
整个应用10. Streaming
传统 SSR:
Server
↓
等待全部数据
↓
HTML
↓
BrowserStreaming:
Server
↓
Shell
↓
Browser
数据完成
↓
继续发送例如:
<Suspense fallback={<Loading />}>
<ProductList />
</Suspense>可以让:
页面骨架先到达浏览器。
11. Next.js 的真正价值
Next.js 不应该只理解成:
React 路由框架。
更准确的是:
React + Rendering + Routing + Data Fetching + Caching + Streaming + Deployment因此理解 Next.js 必须理解:
请求
↓
路由
↓
Server Component
↓
Data
↓
Cache
↓
Render
↓
HTML / RSC Payload
↓
Browser12. Next.js 性能优化不能只看 Bundle
现代 Next.js 应用需要同时看:
Server
├── TTFB
├── Data Fetch
├── Cache
└── Render
Browser
├── JS
├── Hydration
├── Interaction
└── Paint例如:
TTFB 很快并不意味着:
LCP 很快因为浏览器拿到 HTML 后仍然需要:
Parse
↓
CSS
↓
Image
↓
Font
↓
Layout
↓
Paint13. Server Actions / Server Functions 应该如何理解?
它们的核心思想不是:
“不用 API 了。”
而是:
让客户端可以调用服务器端函数,同时由框架处理网络边界和序列化。
因此开发体验类似:
Function Call但底层仍然存在:
Network Boundary这意味着:
服务器函数参数仍然必须视为不可信输入。
不能因为:
'use server'就认为客户端无法伪造请求。
14. React 性能优化最容易犯的错误
看到:
组件重新渲染就开始:
useMemo()
useCallback()
memo()这是错误的优化方式。
正确流程:
发现性能问题
↓
Profile
↓
找到瓶颈
↓
确定原因
↓
优化
↓
再次测量而不是:
看到 render
↓
memo