服务端渲染在JavaScript中的本质是代码在Node.js中执行并生成HTML字符串;Next.js的getServerSideProps每次请求时服务端执行并注入props;SSR与hydration断连主因是服务端与客户端渲染不一致;App Router中SSR变为隐式async组件执行,fetch默认缓存。
服务端渲染(SSR)不是框架的专利,而是指 JavaScript 代码在 Node.js 环境中执行并生成 HTML 字符串,再返回给浏览器。关键在于:DOM 操作不能依赖浏览器全局对象(如 window、document),否则会抛出 ReferenceError。
纯手写 SSR 需要手动处理:
React.renderToString 或 Vue.renderToString 捕获window.__INITIAL_STATE__)getServerSideProps 是怎么工作的?这是 Next.js 实现 SSR 的核心约定函数,每次请求都会在服务端执行,返回的数据会作为 props 注入页面组件。它不是“预渲染”,而是真正意义上的请求时服务端执行。
注意点:
pages/xxx.js 或 app/xxx/page.js 的旧版 pages 目录下)中有效app 目录的默认 Server Components 中直接调用(新版用 async 组件 + fetch 替代)fs.readFile 等 Node.js API,但不能访问 window
{ props: { ... } } 对象,否则页面会 500export async function getServerSideProps(context) {
const res = await fetch('https://api.example.com/user')
const user = await res.json()
return {
props: { user } // 这个对象会变成组件的 props
}
}
最常见问题是服务端渲染内容与客户端初次 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(比如动态插入广告位),但服务端没做对应输出StyleSheetManager 或未同步服务端的 class name 生成逻辑app 目录)下 SSR 的实际形态变了Next.js 13+ 的 app 目录默认使用 Server Components,不再需要 getServerSideProps。SSR 变成隐式行为:所有 async 组件函数都在服务端执行,await fetch 自动被拦截并去重、缓存。
但这也带来新约束:
useState、useEffect、浏览器 API"use client" 标记的 Client Components 中fetch 默认开启 cache: 'force-cache',要实现真正的 SSR(每次请求都发新请求),得显式写 fetch(url, { cache: 'no-store' })
真实项目里,很多人卡在“为什么 app 目录下接口没重发”,其实只是 fetch 缓存策略默认变了。