哈希类名不一致是SSR水合闪烁的直接原因,因服务端与客户端cache实例未复用、哈希输入项(key/stylisPlugins/nonce)不一致、客户端未调用cache.reset()及动态插值不可序列化所致。

哈希类名不一致是 SSR 注水(hydration)时闪烁的直接原因,不是 Emotion 本身有问题,而是服务端和客户端生成 class 名的哈希输入没对齐。
cache 实例必须完全复用,不能新建
Emotion 的 css 函数依赖 cache 实例做哈希计算。服务端用一个 cache,客户端又调用 createCache() 新建一个,哪怕参数一模一样,哈希结果也不同。
- 服务端渲染必须传入同一个
cache实例给CacheProvider和renderStylesToString - 客户端入口里不能直接
createCache(),而要从服务端注入的全局变量或 context 中复用(如通过window.__emotion_cache) - 若用 Next.js App Router,需在自定义
RootLayout中统一提供 cache,并确保服务端组件和客户端组件共享同一实例
哈希输入项缺一不可:stylisPlugins、key、nonce 都要一致
Emotion 的哈希算法会把 cache.key、cache.stylisPlugins、cache.nonce 全部纳入输入。任一不同,class 名就变。
-
key必须相同(默认css),不能服务端设key: 'ssr',客户端漏配 -
stylisPlugins若服务端启用了@emotion/babel-plugin压缩,客户端没配同等插件,规则解析顺序不同,哈希必然错 -
nonce在 CSP 场景下必须透传到客户端,否则服务端插入的<style data-emotion="css" nonce="abc">和客户端生成的nonce="def"不匹配,样式被浏览器丢弃
SSR 提取后必须清空客户端 cache,否则水合前就污染了哈希空间
服务端执行 renderStylesToString 时,会往 cache 里写入所有已生成的规则;客户端 hydration 前若不清空,再调用 css 就可能复用旧哈希值——但此时 DOM 已有服务端 class 名,而新生成的样式规则却指向另一个 class,导致样式“找不到”。
立即学习“前端免费学习笔记(深入)”;
- 客户端首次渲染前必须调用
cache.reset()(仅 SSR 场景需要) - 不要在组件内调用
cache.reset(),应在hydrateRoot或createRoot之前执行 - 若使用
@emotion/serverv11,注意它和@emotion/react主版本必须严格一致,否则reset()可能失效
动态插值内容必须服务端可序列化,否则哈希输入不等价
像 css({ color: props.theme?.primary }) 这种写法,如果服务端没传 theme 或传的是空对象,而客户端有完整 theme,哈希输入字符串就不同。
- 服务端必须保证所有插值字段都存在且值确定,不能靠 localStorage 或 useEffect 补充
- 避免在插值中使用函数、Symbol、undefined 等无法 JSON 序列化的值,Emotion 会静默转成空字符串,导致哈希失真
- 主题切换类逻辑应放在 CSS 变量层,而非 class 名生成层;class 名只承载结构,不承载状态
最容易被忽略的是 cache.reset() 的时机和 stylisPlugins 的一致性——前者漏掉,水合后样式全乱;后者不一致,连开发环境都可能跑出两套 class 名,上线才暴露问题。


















