前言:除了你,没人愿意对着空白屏幕发呆
接上回。咱们用虚拟滚动把 10 万条数据治得服服帖帖。现在你的应用在渲染出来之后,性能确实很顶。
但是,有一个更尴尬的问题摆在面前:首屏加载(First Paint)。
咱们之前写的 React(CSR - 客户端渲染),工作流程是这样的:
- 浏览器请求页面。
- 服务器扔回来一个几乎是空的 HTML:
<div id="root"></div>。 - 浏览器加载那个 5MB 的 JS 包。
- JS 执行,去调 API,拿到数据。
- JS 把 DOM 算出来,塞进
root里。
这一套下来,用户在前几秒钟看到的都是大白屏 。 更惨的是 SEO(搜索引擎优化)。Google 的爬虫虽然聪明点了,但百度的爬虫那是相当"直男"。它过来一看:"哟,这网页只有一个 div?内容是空的?垃圾!" 然后转身就走,你的网页在搜索结果里永远排在第 100 页开外。
今天,我们要来聊聊 Next.js 和 服务端渲染 (SSR)。我们要把渲染这脏活累活从用户的浏览器挪到服务器上,让用户打开网页的一瞬间,内容就是满的。
Image of CSR vs SSR architecture diagram
观念升级:不仅是框架,是"元框架"
很多兄弟对 Next.js 有误解,觉得它就是个"带路由的 React"。 错!在 2025 年的今天,Next.js 其实是 React 的完全体。
React 官方团队现在都明说了:"你要写 React,推荐直接用 Next.js。" 为什么?因为 React 只是个 UI 库(View 层),而 Next.js 帮你搞定了路由、打包、SSR、API 路由、图片优化......它是全家桶。
我们重点要说的,是它的核心大招:React Server Components (RSC)。
实战演练:从 useEffect 到 async/await
在传统的 CSR 项目里,我们要获取数据,通常得写个 useEffect,还得处理 loading 状态。
❌ 传统的 CSR 写法(慢、且 SEO 为 0):
tsx
// 客户端组件
import { useState, useEffect } from 'react';
export default function UserProfile({ id }) {
const [user, setUser] = useState(null);
useEffect(() => {
// 浏览器加载完 JS 后才开始发请求
fetch(`/api/users/${id}`).then(res => res.json()).then(setUser);
}, [id]);
if (!user) return <div>Loading...</div>; // 用户先看 1 秒钟这个
return <h1>{user.name}</h1>;
}
✅ Next.js (App Router) 写法(秒开、SEO 满分):
在 Next.js 的 App Router 模式下,组件默认就是服务端组件 (Server Component) 。这意味着你可以直接在组件里写 async/await,直接连数据库!
//
// 这代码是在服务器上跑的!浏览器连一行 JS 都收不到,只收到 HTML。
import db from '@/lib/db';
async function getUser(id) {
// 直接查库,或者调内网 API,快得飞起
return await db.user.findUnique({ where: { id } });
}
export default async function UserPage({ params }) {
const user = await getUser(params.id);
// 服务器直接把渲染好的 HTML 扔给浏览器
// 爬虫看到的就是:<h1>Jack</h1>
return (
<main>
<h1>{user.name}</h1>
<p>{user.bio}</p>
</main>
);
}
发生了什么?
- 用户请求
/user/1。 - Next.js 服务器接收请求,执行
getUser,拿到数据。 - Next.js 生成完整的 HTML:
<main><h1>Jack</h1>...</main>。 - 浏览器收到 HTML,直接显示内容。
没有 Loading 转圈,没有白屏。爬虫一看:"哇,内容好丰富!" 排名蹭蹭往上涨。
进阶技巧:静态生成 (SSG) ------ 也就是"作弊"
对于"用户个人主页"这种数据会变的页面,我们用 SSR(每次请求都跑一遍服务器)。 但对于"关于我们"、"博客文章"这种一万年不改一次的页面,如果每次有人访问都去查数据库,那就太冤大头了。
Next.js 有个无敌的功能叫 SSG (Static Site Generation) 。
你只需要告诉 Next.js:"这页面是静态的。" 它就会在构建打包 (Build Time) 的时候,把这个页面生成为一个 .html 文件。
当用户访问时,Nginx/Vercel 直接把这个 HTML 扔出去。这比 SSR 还要快,因为连数据库都不用查,甚至不需要服务器计算,这就是纯静态资源分发。
//
// 告诉 Next.js 提前把哪些文章生成好 HTML
export async function generateStaticParams() {
const posts = await getposts();
return posts.map((post) => ({
slug: post.slug,
}));
}
export default async function BlogPost({ params }) {
// ... 渲染逻辑
}
避坑指南:Hydration(注水)之痛
虽然 SSR 出来的 HTML 能立马看到,但它一开始是"死"的(没有交互)。 浏览器展示 HTML 后,会加载 JS,把 React 的事件监听器(onClick 等)挂载上去,这个过程叫 Hydration(注水) 。
如果你在服务端渲染的内容,和客户端注水时的内容不一致 ,React 就会报错: Text content does not match server-rendered HTML.
常见翻车现场:
export
// ❌ 错误!
// 服务器时间是 UTC,客户端时间是 GMT+8
// HTML 里是 10:00,JS 算出来是 18:00,直接报错
return <div>Current time: {new Date().toLocaleTimeString()}</div>;
}
解决办法: 凡是涉及到浏览器特有属性(window、localStorage、时区)的,必须放到 useEffect 里,或者用 dynamic import 强制转为客户端组件。
'use
总结:该不该上 Next.js?
如果你的项目是:
- 后台管理系统(Dashboard) :无所谓,CSR 就够了,没人靠 SEO 搜后台。
- 企业官网 / 博客 / 电商 / 资讯站 :必须上 Next.js。不上就是跟钱过不去,跟流量过不去。
SSR 解决了首屏速度,SSG 解决了高并发压力,Server Components 让前端能直接操作后端逻辑。 这就叫降维打击。
好了,我要去把那个还是 create-react-app 的老官网迁移到 Next.js 了,为了那该死的 Google 排名。

下期预告 :既然我们都已经能在 Server Components 里直接读数据库了,那是不是连 API 接口都不用写了? 没错!写
axios.post已经是过去式了。 下一篇,我们来聊聊 "Server Actions:全栈 React 的最后一公里" 。教你如何在按钮的onClick里直接调用服务器函数,把"前后端分离"重新"合二为一"。