我的文档库

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 D

Fiber:

Fiber A
 ↓
Fiber B
 ↓
Fiber C
 ↓
可暂停
 ↓
继续

于是 React 可以根据优先级决定:

用户输入 > 动画 > 普通更新 > 低优先级更新

3. Render 和 Commit

React 更新大体可以理解为:

Update
  ↓
Render Phase
  ↓
Commit Phase

Render 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
 ↓
Hydration

Server Components 则进一步把:

Component

本身分成:

Server Component
Client Component

Server Component:

Server
 ↓
执行
 ↓
结果发送给客户端

Client Component:

Browser
 ↓
JavaScript
 ↓
Interactive

7. 为什么 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
 ↓
Browser

Streaming:

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
 ↓
Browser

12. Next.js 性能优化不能只看 Bundle

现代 Next.js 应用需要同时看:

Server
 ├── TTFB
 ├── Data Fetch
 ├── Cache
 └── Render

Browser
 ├── JS
 ├── Hydration
 ├── Interaction
 └── Paint

例如:

TTFB 很快

并不意味着:

LCP 很快

因为浏览器拿到 HTML 后仍然需要:

Parse
 ↓
CSS
 ↓
Image
 ↓
Font
 ↓
Layout
 ↓
Paint

13. Server Actions / Server Functions 应该如何理解?

它们的核心思想不是:

“不用 API 了。”

而是:

让客户端可以调用服务器端函数,同时由框架处理网络边界和序列化。

因此开发体验类似:

Function Call

但底层仍然存在:

Network Boundary

这意味着:

服务器函数参数仍然必须视为不可信输入。

不能因为:

'use server'

就认为客户端无法伪造请求。


14. React 性能优化最容易犯的错误

看到:

组件重新渲染

就开始:

useMemo()
useCallback()
memo()

这是错误的优化方式。

正确流程:

发现性能问题
 ↓
Profile
 ↓
找到瓶颈
 ↓
确定原因
 ↓
优化
 ↓
再次测量

而不是:

看到 render
 ↓
memo

本页目录