服务端未提取关键css且客户端重复注入是样式丢失最常见原因;必须在服务端调用renderstylestostring或extractcritical并将结果注入html head,next.js需在getserversideprops等中处理,remix需设置content-type: text/html,同时确保服务端与客户端复用同一emotion cache实例并稳定动态样式引用。

服务端没提取关键 CSS,客户端重复注入
这是样式丢失最常见原因:服务端生成的 HTML 里没有内联 <style></style> 标签,客户端 hydration 时才执行所有 css 调用,导致 FOUC 或 layout shift。
必须在服务端调用 renderStylesToString 或 extractCritical,把结果塞进 HTML 的 中。Next.js 用户不能跳过 getServerSideProps 或 getStaticProps 里的样式提取步骤;Remix 用户需确保 Content-Type: text/html 已设置,否则 extractCritical 拿不到完整样式树。
- 错误做法:只在客户端用
CacheProvider,服务端完全不处理样式提取 - 正确做法:服务端调用
renderStylesToString(ctx.renderPage()),返回的styles注入到的<style data-emotion></style>中 - 注意:
renderStylesToString必须配合@emotion/server,且不能和extractCritical混用
服务端与客户端 cache 实例不一致
SSR 时样式类名哈希值必须和服务端完全一致,否则客户端 hydration 后 class 名对不上,样式就“消失”了——实际是渲染出了但没匹配上规则。
核心是复用同一个 cache 实例:服务端创建唯一实例并传给 CacheProvider;客户端不能 new emotionCache(),得从模块缓存或全局变量里取同一份。
- 常见坑:服务端多个请求共用一个未清空的
cache,样式互相污染 - Next.js 用户注意:
getInitialProps中初始化的cache必须透传到App和Document,不能各自 new - 客户端若用了不同
key初始化CacheProvider(比如 dev 环境加了 hash),hydration 会失败
动态样式没稳定引用,服务端缓存失效
css 是纯函数,但每次 render 都新建对象字面量(比如 css({ color: props.primary ? 'blue' : 'gray' })),会导致服务端无法命中缓存,重复计算哈希、序列化、插入 style 标签。
这不是写法错,而是没意识到缓存依赖引用稳定性。服务端每请求都重跑一遍,内存抖动 + 多次 insertRule,样式可能漏掉或错乱。
- 错误写法:在组件内部反复调用
css({...}),哪怕参数一样 - 推荐方案:用
styled替代,它内部已做 memoization;或把css提到组件外,动态部分用useMemo包裹 - 特别注意:hover 等伪类在 SSR 时不执行 JS,若没启用
ssr: true配置或没写进服务端提取逻辑,首次渲染就会缺失
数组合并样式对象被误传给 css 函数
css 函数不接受数组参数,但很多人误写成 css([base, hover]),结果控制台无报错、样式却丢失,甚至触发 TypeError: Cannot convert a Symbol value to a string。
Emotion 不解析数组,也不会自动合并对象。你传进去的是数组,它就尝试转字符串——而内部 Symbol 引用一转就崩。
- 正确做法:用扩展运算符合并,如
css({ ...base }, { ...hover }) - 动态数组要先
Object.assign({}, ...styleArray)再传入 - 避免在
styled模板字符串里展开数组:styled.div`${stylesArray}`是非法的
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











