emotion 的样式注入更轻量、缓存更激进、ssr 提取更精准。它用单个 style 标签和两级缓存优化性能,ssr 时按渲染树提取关键 css;styled-components 标签冗余多、动态样式计算开销大、ssr 易漏提。

Emotion 的样式注入机制更轻量
Emotion 默认使用 @emotion/sheet 管理样式表,它把 CSS 规则直接写入单个 <style></style> 标签,并支持动态追加、复用和批量 flush。而 Styled-components 为每个组件实例或样式块生成独立的 <style></style> 标签(尤其在 dev 模式下),标签数量随组件渲染次数线性增长,触发更多 DOM 操作和浏览器重排。
实操建议:
- 在大量列表项(如
map渲染百级styled.div)中,Styled-components 可能造成 style 标签数暴增,Chrome DevTools 的 Elements 面板里能看到数十甚至上百个<style data-styled> 标签</style> - Emotion 的
sheet是可替换的,你可用CacheProvider+ 自定义sheet控制插入位置或禁用动态注入,这对 SSR 或微前端隔离很关键 - 不要依赖 dev 模式下的性能表现——Styled-components 在 production 中会合并部分样式,但 Emotion 的注入路径始终更短
Emotion 的运行时缓存策略更激进
Emotion 内置 @emotion/memoize 和 @emotion/weak-memoize,对字符串模板和对象样式都做两级缓存:第一层是基于 AST 的静态哈希,第二层是基于 props 引用的弱映射。Styled-components 主要靠字符串模板字面量的静态哈希去重,对带函数插值的动态样式(如 color: ${props => props.theme.primary})只能在每次 render 时重新计算并比对结果。
常见错误现象:
- 用
styled.button包裹一个频繁 re-render 的组件,且样式含 props 函数,CPU 占用明显高于同等 Emotionstyled.button(注意:Emotion 的styled也走同套缓存,但底层比 Styled-components 多一层弱引用兜底) - 在 React 18 并发渲染下,Styled-components 的样式计算可能被中断重试多次;Emotion 的 memoize 能更好适配
useTransition场景
服务端渲染时 Emotion 的关键 CSS 提取更精准
Emotion 的 @emotion/server/extract-critical 是基于实际渲染树做样式收集,只提取真正用到的规则;Styled-components 的 ServerStyleSheet 依赖组件挂载时的静态分析,在高阶组件、条件渲染或 lazy 组件中容易漏提或误提。
使用场景举例:
- Next.js App Router 中有
dynamic(import(...))加载的按钮组件,Emotion 能在 hydration 前拿到其真实样式;Styled-components 可能返回空 CSS 或 fallback 到全局注入 - 提取结果体积差异可达 30%+,尤其当项目含大量主题变体或暗色模式开关时
- Emotion 支持
renderStylesToString同步提取,无需等待 Promise,更适合边缘函数(Edge Functions)环境
为什么你未必需要立刻切换
Emotion 的优势集中在高频更新、强 SSR、微前端或极端体积敏感的场景。如果你的项目是中后台管理后台、DOM 节点不多、无服务端渲染需求,Styled-components 的成熟生态(如 styled-system 集成、Theme UI 支持)和调试体验(DevTools 显示组件名而非 hash)反而更省心。
真正容易被忽略的一点:两者在 React Server Components(RSC)中都不支持动态样式函数(即不能在组件内写 ${props => ...}),必须提前 resolve。这个限制比性能差异更早卡住你。










