Appearance
第2章:React Server 技术演进历程
2.1 React SSR 发展时间线
技术演进时间轴
2.2 React 15-16 时代:传统 SSR 的奠基
React 15 的 SSR 实现
在 React 15 时代,SSR 的实现相对简单但功能有限:
javascript
// React 15 SSR 示例
import React from 'react';
import { renderToString } from 'react-dom/server';
import express from 'express';
const app = express();
// 简单的组件
class App extends React.Component {
render() {
return (
<div>
<h1>Hello, {this.props.name}!</h1>
<p>Welcome to React 15 SSR</p>
</div>
);
}
}
app.get('/', (req, res) => {
// 同步渲染,阻塞式
const html = renderToString(<App name="World" />);
res.send(`
<!DOCTYPE html>
<html>
<head><title>React 15 SSR</title></head>
<body>
<div id="root">${html}</div>
<script src="/bundle.js"></script>
</body>
</html>
`);
});
React 15 SSR 的限制:
- 同步渲染:
renderToString
是同步的,会阻塞事件循环 - 无流式支持:必须等待整个组件树渲染完成
- 错误处理简陋:一个组件出错会导致整个渲染失败
- 性能问题:大型应用渲染时间长
React 16 Fiber 架构的革命
React 16 引入的 SSR 改进:
javascript
// React 16 流式 SSR
import { renderToNodeStream } from 'react-dom/server';
import { PassThrough } from 'stream';
app.get('/', (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
// 发送 HTML 头部
res.write(`
<!DOCTYPE html>
<html>
<head><title>React 16 Streaming SSR</title></head>
<body>
<div id="root">
`);
// 流式渲染组件
const stream = renderToNodeStream(<App />);
stream.pipe(res, { end: false });
stream.on('end', () => {
res.write(`
</div>
<script src="/bundle.js"></script>
</body>
</html>
`);
res.end();
});
stream.on('error', (error) => {
console.error('SSR Error:', error);
res.end('Server Error');
});
});
2.3 React 18 时代:Concurrent Features 与 Streaming SSR
Concurrent Features 架构图
React 18 Streaming SSR 详解
javascript
// React 18 现代 SSR 实现
import { renderToPipeableStream } from 'react-dom/server';
import { Suspense } from 'react';
// 支持 Suspense 的组件
function App() {
return (
<html>
<head>
<title>React 18 Streaming SSR</title>
</head>
<body>
<div id="root">
<Header />
<Suspense fallback={<div>Loading main content...</div>}>
<MainContent />
</Suspense>
<Suspense fallback={<div>Loading sidebar...</div>}>
<Sidebar />
</Suspense>
<Footer />
</div>
</body>
</html>
);
}
// 异步组件示例
async function MainContent() {
// 模拟数据获取
const data = await fetchMainData();
return <main>{data.content}</main>;
}
async function Sidebar() {
const data = await fetchSidebarData();
return <aside>{data.widgets}</aside>;
}
// 服务器端渲染
app.get('/', (req, res) => {
const stream = renderToPipeableStream(<App />, {
// Shell 准备就绪时立即发送
onShellReady() {
res.statusCode = 200;
res.setHeader('Content-type', 'text/html');
stream.pipe(res);
},
// 所有内容准备就绪
onAllReady() {
// 对于爬虫,等待所有内容
if (req.headers['user-agent']?.includes('bot')) {
res.statusCode = 200;
res.setHeader('Content-type', 'text/html');
stream.pipe(res);
}
},
// 错误处理
onError(error) {
console.error('SSR Error:', error);
res.statusCode = 500;
}
});
});
Streaming SSR 工作流程
选择性 Hydration (Selective Hydration)
javascript
// 客户端 Hydration 代码
import { hydrateRoot } from 'react-dom/client';
import { Suspense } from 'react';
// React 18 会智能地选择 Hydration 顺序
function ClientApp() {
return (
<div>
<Header /> {/* 立即 Hydration */}
<Suspense fallback={<div>Loading...</div>}>
<MainContent /> {/* 数据到达后 Hydration */}
</Suspense>
<Suspense fallback={<div>Loading...</div>}>
<InteractiveWidget /> {/* 用户交互时优先 Hydration */}
</Suspense>
<Footer /> {/* 最后 Hydration */}
</div>
);
}
// 客户端入口
const root = hydrateRoot(document.getElementById('root'), <ClientApp />);
选择性 Hydration 的优势:
- 优先级驱动:用户交互的组件优先 Hydration
- 非阻塞:不需要等待所有组件都准备好
- 渐进式:页面逐步变得可交互
- 性能优化:减少 TTI (Time to Interactive)
2.4 React Server Components 的诞生
RSC 概念的提出背景
RSC 与传统 SSR 的根本区别
RSC 架构设计原理
React Flight Protocol 详解
javascript
// Flight Protocol 数据格式示例
// 这是 RSC 序列化后的数据格式
// 1. 服务器组件渲染结果
const flightData = {
// 组件树结构
tree: {
type: 'div',
props: {
children: [
{
type: 'h1',
props: { children: 'Welcome to RSC' }
},
{
// 客户端组件引用
type: Symbol.for('react.client.reference'),
props: { userId: 123 },
$$typeof: Symbol.for('react.element'),
_payload: {
name: 'InteractiveButton',
id: './components/InteractiveButton.js'
}
}
]
}
},
// 客户端组件映射
clientReferences: {
'./components/InteractiveButton.js': {
id: 'chunk-123',
chunks: ['chunk-123.js'],
name: 'InteractiveButton'
}
}
};
// 2. 客户端接收和处理
function processFlightData(flightData) {
// 反序列化组件树
const tree = deserializeTree(flightData.tree);
// 加载客户端组件
const clientComponents = loadClientComponents(
flightData.clientReferences
);
// 渲染混合组件树
return renderMixedTree(tree, clientComponents);
}
2.5 现代框架的 RSC 集成
Next.js App Router 架构
Next.js RSC 实现示例
javascript
// app/page.js - Server Component
import { Suspense } from 'react';
import { UserProfile } from './components/UserProfile';
import { InteractiveChat } from './components/InteractiveChat';
// 这是一个 Server Component
export default async function HomePage({ searchParams }) {
// 直接在服务器获取数据
const user = await db.users.findById(searchParams.userId);
const posts = await db.posts.getRecent(10);
return (
<div className="homepage">
<header>
<h1>欢迎回来, {user.name}!</h1>
</header>
<main>
{/* Server Component - 不会发送到客户端 */}
<UserProfile user={user} />
{/* Suspense 边界 */}
<Suspense fallback={<div>加载帖子中...</div>}>
<PostList posts={posts} />
</Suspense>
{/* Client Component - 需要交互 */}
<InteractiveChat userId={user.id} />
</main>
</div>
);
}
// app/components/UserProfile.js - Server Component
export async function UserProfile({ user }) {
// 可以直接访问数据库或 API
const stats = await db.users.getStats(user.id);
return (
<section className="user-profile">
<img src={user.avatar} alt={user.name} />
<div>
<h2>{user.name}</h2>
<p>帖子: {stats.postCount}</p>
<p>关注者: {stats.followers}</p>
</div>
</section>
);
}
// app/components/InteractiveChat.js - Client Component
'use client';
import { useState, useEffect } from 'react';
export function InteractiveChat({ userId }) {
const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState('');
useEffect(() => {
// 建立 WebSocket 连接
const ws = new WebSocket(`/api/chat/${userId}`);
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
setMessages(prev => [...prev, message]);
};
return () => ws.close();
}, [userId]);
const sendMessage = () => {
if (newMessage.trim()) {
// 发送消息逻辑
fetch('/api/chat/send', {
method: 'POST',
body: JSON.stringify({ userId, message: newMessage })
});
setNewMessage('');
}
};
return (
<div className="chat-widget">
<div className="messages">
{messages.map(msg => (
<div key={msg.id} className="message">
{msg.content}
</div>
))}
</div>
<div className="input-area">
<input
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
placeholder="输入消息..."
/>
<button onClick={sendMessage}>发送</button>
</div>
</div>
);
}
构建过程详解
2.6 技术演进的关键节点总结
性能指标对比
开发体验演进
特性 | React 15 | React 16 | React 18 | RSC |
---|---|---|---|---|
渲染方式 | 同步阻塞 | 流式渲染 | 并发渲染 | 混合渲染 |
错误处理 | 基础 | Error Boundaries | 改进 | 细粒度 |
代码分割 | 手动 | 动态导入 | Suspense | 自动 |
数据获取 | 客户端 | 混合 | 改进 | 服务器优先 |
Bundle 大小 | 大 | 大 | 大 | 最小化 |
开发复杂度 | 中等 | 中等 | 较高 | 简化 |
未来发展趋势
小结
本章回顾了 React Server 技术从 React 15 到现在的完整演进历程:
- React 15-16:奠定了 SSR 的基础,引入了流式渲染
- React 18:带来了并发特性和选择性 Hydration
- RSC 时代:革命性地改变了前后端代码的组织方式
- 现代框架:Next.js 等框架让 RSC 在生产环境中可用
下一章我们将深入探讨 React Server 的基础概念,包括组件分类、渲染机制等核心知识。
