css-in-js性能差源于高频渲染下重复哈希、insertrule、style标签创建及内存泄漏;需用useinsertioneffect预埋规则、复用标签、缓存hash,并将静态样式外提至css文件。

大型应用中 CSS-in-JS 性能差,不是因为“用了 JS 写 CSS”这个动作本身,而是它在高频渲染、深度嵌套、动态插值和组件反复挂载卸载时,把运行时开销放大到了不可忽视的程度——比如每次 re-render 都重算哈希、重复 insertRule、不断创建 <style></style> 标签,甚至泄漏内存。
高频渲染下样式重复注入导致 layout thrashing
React 18 的并发渲染 + 虚拟滚动/实时图表等场景中,组件可能每秒重渲染数十次。此时若用 styled.div 或 css 函数直接包裹动态 props(如 ${props => props.active && 'background: blue;'}),每次都会触发整块样式重新解析、哈希重算、insertRule 调用,浏览器被迫同步重排。
-
useEffect注入太晚,样式滞后于 DOM 变更,出现跳动或伪类响应延迟 -
useLayoutEffect虽同步,但 DOM 已更新完毕,插入<style></style>仍会强制重排 - 真正安全的时机只有
useInsertionEffect:它在 DOM diff 完成、真实 DOM 修改前执行,可预埋规则 - 但仅靠它不够——必须复用
<style></style>标签、按 hash 缓存规则、避免重复insertRule
深层嵌套 + 动态插值让样式无法缓存
像 styled.button`&:hover { &__icon { transform: ${props => props.flipped ? 'scaleX(-1)' : 'none'}; } }` 这种写法,表面是嵌套语法,实际生成的是高特异性、带空格的后代选择器(如 .sc-abc:hover .sc-abc__icon),且因插值耦合 props,整个样式块失去缓存能力。
- 浏览器匹配
.a .b .c时从右往左,节点越多越慢;gzip 对长类名压缩效率也低 - 每次渲染都新建字符串、重算 hash、追加新 rule,内存和 CPU 双吃紧
- 解决方案不是禁用嵌套,而是切断“嵌套 → 动态选择器”的链路:用静态类名(如
btn__icon)+clsx拼接,把变化逻辑移出 CSS 层 - 真正不随 props 变的样式(如
padding、border-radius)应彻底外提,走.css文件由构建工具提取
构建时未剥离运行时导致包体积与内存双膨胀
很多项目没意识到:生产环境里 @emotion/react 或 styled-components 的大部分代码根本不需要。它们被 tree-shaken 不掉,是因为你没关掉开发专用逻辑(如主题订阅、devtool 支持),或误配了零运行时方案。
- Linaria 默认不生成独立 CSS——必须显式配置
outputCss: true,否则退化为普通运行时库 - Vite 中未手动引入生成的
linaria.css,样式就只存在于 JS 字符串里,白费编译 - Emotion 的
cache若未按主题/尺寸等 key 分片,不同主题切换时旧规则不清理,内存持续增长 - Stale
<style></style>标签不清理:组件卸载后,其注入的样式规则仍留在document.head,长期运行后 DOM 中堆积大量无用<style></style>
最常被忽略的一点:性能瓶颈往往不出现在“怎么写样式”,而出现在“谁该负责哪部分样式”。CSS 变量适合响应 props 的简单变更(颜色、尺寸),@keyframes 必须静态定义,复杂交互动画交给 Web Animations API;而所有基础布局、间距、圆角——它们本就不该出现在 JS 里。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











