服务端渲染在JavaScript中的本质是代码在Node.js中执行并生成HTML字符串;Next.js的getServerSideProps每次请求时服务端执行并注入props;SSR与hydration断连主因是服务端与客户端渲染不一致;App Router中SSR变为隐式async组件执行,fetch默认缓存。

服务端渲染在 JavaScript 中的本质是什么?
服务端渲染(SSR)不是框架的专利,而是指 JavaScript 代码在 Node.js 环境中执行并生成 HTML 字符串,再返回给浏览器。关键在于:DOM 操作不能依赖浏览器全局对象(如 window、document),否则会抛出 ReferenceError。
纯手写 SSR 需要手动处理:
- 入口文件需兼容服务端(无浏览器 API 调用)
- 组件状态必须可序列化,且首次渲染结果能被
React.renderToString或Vue.renderToString捕获 - 数据获取逻辑必须在服务端触发,并注入到 HTML 中(如通过
window.__INITIAL_STATE__) - 客户端 hydrate 时需复用服务端生成的 DOM,避免重复渲染闪烁
Next.js 的 getServerSideProps 是怎么工作的?
这是 Next.js 实现 SSR 的核心约定函数,每次请求都会在服务端执行,返回的数据会作为 props 注入页面组件。它不是“预渲染”,而是真正意义上的请求时服务端执行。
注意点:
立即学习“Java免费学习笔记(深入)”;
- 只在页面组件(
pages/xxx.js或app/xxx/page.js的旧版pages目录下)中有效 - 不能在普通组件中使用,也不能在
app目录的默认 Server Components 中直接调用(新版用async组件 +fetch替代) - 函数内可调用数据库、API、
fs.readFile等 Node.js API,但不能访问window - 返回值必须是
{ props: { ... } }对象,否则页面会 500
export async function getServerSideProps(context) {
const res = await fetch('https://api.example.com/user')
const user = await res.json()
return {
props: { user } // 这个对象会变成组件的 props
}
}
Next.js 的 SSR 和客户端 hydration 之间容易断连的点
最常见问题是服务端渲染内容与客户端初次 render 结果不一致,导致 React 报错 Hydration failed because the initial UI does not match what was rendered on the server。
典型诱因:
- 组件内部使用了
Math.random()、Date.now()等非确定性值,服务端和客户端结果不同 - 条件渲染依赖了
typeof window !== 'undefined',服务端为true(因为window不存在),客户端为false,导致 DOM 结构不一致 -
useEffect内修改了服务端已渲染的 DOM(比如动态插入广告位),但服务端没做对应输出 - 样式库(如 styled-components)未正确启用
StyleSheetManager或未同步服务端的 class name 生成逻辑
App Router(app 目录)下 SSR 的实际形态变了
Next.js 13+ 的 app 目录默认使用 Server Components,不再需要 getServerSideProps。SSR 变成隐式行为:所有 async 组件函数都在服务端执行,await fetch 自动被拦截并去重、缓存。
但这也带来新约束:
- Server Components 中不能使用
useState、useEffect、浏览器 API - 交互逻辑必须下沉到
"use client"标记的 Client Components 中 -
fetch默认开启cache: 'force-cache',要实现真正的 SSR(每次请求都发新请求),得显式写fetch(url, { cache: 'no-store' }) - 服务端生成的 HTML 不再包含完整 JS bundle,hydration 更轻量,但也意味着部分逻辑无法在服务端“提前执行”
真实项目里,很多人卡在“为什么 app 目录下接口没重发”,其实只是 fetch 缓存策略默认变了。











