Antd 的 @ant-design/cssinjs 在 SSR 中通过服务端创建唯一 cache、收集样式字符串并注入 HTML 的 <style> 标签,客户端复用同一 cache 完成水合;版本不一致或 cache 复用错误会导致样式丢失或闪屏。

Antd 的 @ant-design/cssinjs 在 SSR 中怎么工作
它不是把样式写进 CSS 文件,而是用 JS 创建样式缓存,在服务端渲染时收集所有组件生成的样式字符串,再注入到 HTML 的 <style> 或 <script> 标签里。客户端水合时,StyleProvider 会复用这个缓存,避免重复计算和样式覆盖。
关键点在于:服务端和客户端必须用同一个 cache 实例(或至少兼容的序列化格式),否则水合后样式会丢失或错乱。
-
createCache()必须在每次请求中新建,不能跨请求复用(如放在模块顶层) -
extractStyle(cache)要在服务端渲染完成后立即调用,且不能带副作用(比如触发重渲染) - 客户端的
StyleProvider必须接收和服务端一致的cache,否则水合时无法匹配已注入的样式
@ant-design/nextjs-registry 为什么有时失效
它本质是封装了 @ant-design/cssinjs 的 StyleProvider + useServerInsertedHTML 逻辑,但它的内部依赖版本必须和项目中 antd 所用的 @ant-design/cssinjs 版本严格对齐。
常见失效现象:Button 渲染出来没颜色、Input 边框消失、主题变量未生效——不是配置错了,而是版本不一致导致缓存结构或提取逻辑错位。
立即学习“前端免费学习笔记(深入)”;
- 运行
npm ls @ant-design/cssinjs查看实际解析出的版本,确认antd和@ant-design/nextjs-registry是否共用同一份子依赖 - 若发现多个版本(比如
antd@5.14.2依赖@ant-design/cssinjs@5.3.0,而@ant-design/nextjs-registry@1.3.0用了5.2.1),就强制统一:用resolutions或降级 registry 到1.2.0 - 注意
@ant-design/nextjs-registryv1.3.0+ 默认启用once: true提取,但某些 antd 组件(如带动画的Tooltip)可能需要多次提取才能捕获全部样式
App Router 下手动实现 RootStyleRegistry 的要点
当 @ant-design/nextjs-registry 不适配当前环境(比如自定义 cache 配置、需支持暗色模式切换),就得手写一个。核心是控制好 useServerInsertedHTML 的执行时机和内容格式。
- 必须加
'use client',否则useServerInsertedHTML无法使用 -
extractStyle(cache, { plain: true, once: true })输出的是纯 CSS 字符串,要包裹在<style>标签里,不能直接塞进<script> - 给
<style>加上data-rc-order="prepend"和id="antd-cssinjs",确保它比客户端运行时插入的样式优先级更高 - 避免在
useServerInsertedHTML回调里做异步操作或读取客户端专属 API(如window),否则构建时报错
为什么加了 suppressHydrationWarning 还闪屏
这个属性只压制 React 的水合不匹配警告,不解决样式缺失本身。真正导致闪屏的是:服务端没注入样式,或客户端注入了不同版本的样式,造成 DOM 层级或 class 名不一致,触发了 React 的水合回退(reconciliation fallback)。
典型误操作:
- 在
layout.tsx外层包了ThemeProvider但没同步处理StyleProvider,导致主题切换逻辑和服务端样式缓存脱节 - 用
dynamic(..., { ssr: false })引入 Antd 组件,绕过了服务端样式收集 -
RootLayout中<AntdRegistry>没包裹完整children(比如漏掉了<header>或自定义<Suspense>区域)
最隐蔽的问题:antd 组件内部用了 useEffect 动态插入样式(如 ConfigProvider 的 locale 注入),这类逻辑在服务端不执行,必然导致水合不匹配。


















