css-in-js首屏加载慢50–200ms是真实观测值,因需js执行后动态注入样式,而传统css可并行下载、预解析、高效缓存,且运行时开销显著更高。

首屏加载时间多出 50–200ms 是常态
不是理论值,是真实项目观测到的典型开销。CSS-in-JS(比如 styled-components 或 @emotion/react)需要在 JS 执行阶段动态生成 <style></style> 标签并注入 DOM,这个过程包含字符串解析、哈希计算、选择器重写、样式插入等多个同步步骤。而传统 CSS(含 CSS Modules)在构建时就已产出静态 .css 文件,浏览器可并行下载、预解析、缓存复用。
关键差异点:
- 传统 CSS 的
link rel="stylesheet"触发浏览器预加载器(preload scanner),JS 还没执行时样式资源已在下载中;CSS-in-JS 的样式生成必须等 JS bundle 解析执行完才开始 - 服务端渲染(SSR)场景下,CSS-in-JS 需额外做样式提取(如
extractCritical或cache.toString()),漏掉或配置错会导致 FOUC(闪白)或重复注入 - 移动端低端设备上,单次组件渲染触发的样式重计算可能比桌面端慢 3–5 倍,尤其当大量使用函数式样式(
props => ({ color: props.primary ? 'blue' : 'gray' }))时
运行时性能:高频更新组件会明显卡顿
CSS-in-JS 的样式对象每次渲染都可能重新计算——哪怕只是 props 里一个布尔值变了,底层库仍要走一遍插值、哈希、查重逻辑。传统 CSS 只需切换 class 名,由浏览器原生处理。
容易被忽略的实操细节:
-
styled.button创建的组件,内部默认不 memoize 样式函数;若传入内联函数(如css={theme => ({ color: theme.text })}),每次 render 都生成新样式对象,强制重注入 - Emotion 的
cssprop 在开发模式下默认开启 source map 支持,会额外增加字符串序列化开销;生产环境务必确认process.env.NODE_ENV === 'production'已生效 - 避免在
useEffect或事件回调里反复调用css()——它不是纯函数,有副作用(创建 style 标签)
缓存失效是隐性大头
传统 CSS 文件可长期缓存(Cache-Control: public, max-age=31536000),只要文件内容不变,hash 就不变。CSS-in-JS 的样式最终注入到 的 <style></style> 标签里,而该标签内容取决于 JS 执行顺序、环境变量、甚至随机 salt(部分库为防冲突引入)。一次无关代码改动,可能导致整页样式 hash 全变。
后果很实际:
- CDN 缓存命中率下降,用户每次更新 JS bundle 都得重新下载全部样式
- HTTP/2 下多个小
<style></style>标签无法像单个.css文件那样高效复用连接 - React 18 的 concurrent rendering 模式下,多次
startTransition可能触发样式重复注入,浏览器需反复重排
别只看包大小,看实际执行路径
有人拿 emotion 的 14KB 和 styled-components 的 16KB 说事,但真正拖慢的是运行时路径长度。一个按钮组件用 styled.button 渲染,背后至少经过:createStyledComponent → generateAndInjectStyles → serializeStyles → insertStyles → getCssText。而传统方案就是 className={styles.button},一行属性赋值。
如果你的项目有这些特征,差距会被放大:
- 大量列表项(如表格、卡片流),每个都用 styled 组件 —— 样式注入次数 = 列表长度
- 使用 CSS-in-JS 实现动画(如
keyframes+animation),每次动画触发都可能重建规则 - 未启用 Babel 插件(如
@emotion/babel-plugin)做编译期优化,所有模板字符串都在运行时解析
最常被跳过的一步:用 Chrome DevTools 的 **Performance** 面板录制一次完整页面加载,过滤出 style 相关任务,看“Recalculate Style”和“Layout”耗时占比——这里的数据比任何 benchmark 都真实。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











