This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
all-in-kingsoft/hhs/REACT/4. 进阶篇/14-Serverside Rendering.md
T
2026-04-29 22:19:51 +08:00

191 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [React, Next.js, SSR, SSG, ISR, Frontend]
create time: 2026-04-29 22:13
---
# Server-Side Rendering
## 概述
服务端渲染(SSR)让 React 组件在服务器端预渲染为 HTML,显著改善首屏加载速度和 SEO。本文档以 Next.js App Router 为核心,介绍 SSR/SSG/ISR 的渲染策略与最佳实践。
## 渲染模式对比
```mermaid
graph TB
subgraph "客户端渲染 CSR"
A[HTML空白页] --> B["下载 JS Bundle"]
B --> C["执行 React hydration"]
C --> D["显示内容"]
end
subgraph "服务端渲染 SSR"
E[请求页面] --> F["服务器渲染 React → HTML"]
F --> G["发送含内容的 HTML"]
G --> H["客户端 hydration"]
H --> I["交互可用"]
end
subgraph "静态生成 SSG"
J["构建时渲染"] --> K["生成纯 HTML 文件"]
K --> L["CDN 分发"]
end
style F fill:#4FC08D,color:#fff
style H fill:#F5A87D,color:#000
style J fill:#61DAFB,color:#000
```
### 三种策略决策表
| 策略 | 适用场景 | 数据时效性 | 构建参与 |
|------|----------|-----------|---------|
| **SSR**(Server Render) | 个性化页面、实时数据 | ✅ 每次请求实时生成 | ❌ |
| **SSG**(Static Site Generation) | 博客、文档、营销页 | ⏱️ 构建时生成 | ✅ |
| **ISR**(Incremental Static Regeneration) | 新闻列表、商品目录 | 🔄 定时后台更新 | ✅(增量) |
## Next.js App Router 架构
```tsx
// app/layout.tsx —— 根布局(所有页面共享)
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="zh">
<body>
<Navbar />
{children}
</body>
</html>
);
}
// app/page.tsx —— 首页(SSR by default)
async function HomePage() {
// ✅ 直接在组件中 await API
const posts = await fetchPosts();
return (
<main>
{posts.map(post => <PostCard key={post.id} post={post} />)}
</main>
);
}
// app/blog/[slug]/page.tsx —— 动态路由页
async function PostPage({ params }: { params: { slug: string } }) {
const post = await getPostBySlug(params.slug);
return <article>{post.content}</article>;
}
```
## Streaming SSR + Suspense
```mermaid
sequenceDiagram
participant Client as 浏览器
participant Server as 服务器
Client->>Server: GET /dashboard
Server->>Server: 并行请求 user/profile/orders
Note over Server: 每个请求可有自己的 Suspense boundary
Server-->>Client: HTML: Navbar + Sidebar<br/>(立即显示,~200ms)
Server-->>Client: Stream: Profile card<br/>(中等优先级,~800ms)
Server-->>Client: Stream: Order history<br/>(低优先级,~1500ms)
Note over Client: 用户体验:渐进式展示,而非等全部完成
```
```tsx
// Dashboard layout(流式渲染的关键)
function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<>
{/* 非关键 UI 用 Suspense 包裹 */}
<Suspense fallback={<SidebarSkeleton />}>
<Sidebar />
</Suspense>
{children}
{/* 各区域独立 Suspense */}
<Suspense fallback={<StatsSkeleton />}>
<StatsPanel />
</Suspense>
<Suspense fallback={<OrdersSkeleton />}>
<OrdersPanel />
</Suspense>
</>
);
}
```
## Data Fetching 策略
```tsx
// 方案1:直接 await(默认缓存 + 共享缓存)
async function Page() {
const data = await fetchData(); // 自动缓存,同路径请求去重
return <div>{data}</div>;
}
// 方案2:带 revalidate 的 ISR
async function Page() {
const data = await fetchData({ next: { revalidate: 60 } }); // 60秒后后台重新验证
return <div>{data}</div>;
}
// 方案3:no-store(强制 SSR,不走缓存)
async function Page() {
const data = await fetchData({ cache: "no-store" }); // 每次请求都获取最新数据
return <div>{data}</div>;
}
// 方案4:client component 中的 fetch(使用 TanStack Query)
"use client";
function Page() {
const { data } = useQuery({ queryKey: ["data"], queryFn: () => fetch("/api/data").then(r => r.json()) });
return <div>{data}</div>;
}
```
## SSR vs Client Component 边界
```tsx
// server component(默认,无需声明)
async function ServerComponent() {
// ✅ 可以直接访问数据库、API密钥、文件系统
const db = await dbConnection.query("SELECT * FROM users");
return <UsersList users={db} />;
}
// client component(需显式声明)
"use client";
function InteractiveChart() {
// ✅ 可以使用 useState/useEffect/DOM API
const [zoom, setZoom] = useState(1);
useEffect(() => { ... }, []);
return <canvas ref={canvasRef} />;
}
// Parent
function Dashboard() {
return (
<>
<ServerComponent /> {/* 在服务端渲染 */}
<InteractiveChart /> {/* 在客户端渲染 */}
</>
);
}
```
> [!warning] Client → Server 通信限制
> - Client Component 无法直接调用 Server Component 的方法或 props
> - 解决方案:通过 URL 参数、cookies、或后端 API 传递数据
## 关联笔记