能彻底移除运行时样式注入逻辑——linaria和vanilla-extract在构建期生成CSS,完全消除insertRule、style标签动态插入等主线程同步操作,避免拖慢React commit阶段、掉帧及强制同步布局问题。
直接用
或
替换
或
,能彻底移除运行时样式注入逻辑——不是“优化”,是让那部分代码根本不出现在浏览器里。
为什么 insertRule 和 style 标签插入是性能瓶颈
每次组件渲染,
都要调用
、做哈希计算、拼接字符串、查重、插入
标签。这些全在主线程同步执行,尤其在列表滚动或动画帧中,会直接拖慢 commit 阶段。Chrome DevTools 的 Performance 面板里能看到大量
调用堆叠,Lighthouse 也会报“避免强制同步布局”。
常见错误现象:
React DevTools 显示某组件 mount 耗时中,
占比异常高(>40%)
移动端滑动时掉帧,但 CPU 占用率明显高于纯 DOM 操作场景
用
包住组件挂载,发现
相关子任务始终存在
linaria 的 Babel 插件配置要点
在 Babel 编译阶段静态分析
模板字面量,生成类名和对应 CSS 文件。它不依赖运行时,但对 JS 表达式有硬性限制。
立即学习
“
前端免费学习笔记(深入)
”;
php版微信js-sdk支付接口类
php版微信js-sdk支付接口类
下载
只支持编译期可求值的表达式:允许
(
是常量对象),禁止
(props 是运行时变量)
不能用动态 import 或变量拼接路径,比如
会导致构建失败
需确保 Webpack/Vite 正确处理生成的 CSS:Vite 要开
;Webpack 要配
,否则 CSS 会漏掉或内联进 JS
调试时别依赖浏览器里找
类名——它只是开发时 fallback,生产环境走的是真实 CSS 文件
vanilla-extract 的 .
css
.ts 文件约束
要求样式必须写在
文件里,不能塞进 React 组件内部。它靠 TypeScript 类型系统做静态分析,生成的 CSS 是真正零 runtime 的产物。
不支持闭包、函数调用、任意 JS 表达式:例如
这种变量若未在编译期确定值,会报类型错误
主题切换必须用
+ 多份预生成 CSS,不能靠 runtime 切 class 或修改 CSS 变量
生成的 CSS 文件路径由
或 Vite 插件控制,需检查打包后 output 目录是否含
文件,且被 HTML 正确引用
VS Code 中能获得完整的类型提示和自动补全,但代价是开发时不能“边写组件边写样式”
怎么确认 runtime 开销真没了
不能只看 bundle size 小了没——关键是浏览器里有没有还干那些事。
打开 Chrome DevTools → Performance → 录制一次交互 → 搜索
、
、
,结果应几乎为 0
在
或
的
方法上打断点,确认它们不再被触发
对比前后帧:用
和
包住组件挂载过程,观察 style 相关子任务是否从 Performance 面板的 commit 阶段消失
最易被忽略的一点:CSS 文件虽已提取,但如果没被正确加载(比如路径 404、
被误删、HTTP/2 push 配置错误),浏览器会回退到 runtime 注入,此时所有优化都白做。
linariavanilla-extractstyled-components@emotion/reactstyled-componentssheet.insertRule()insertRulecommitperformance.mark()stylelinariacsscss`color: ${theme.primary}`theme.primarycss`color: ${props.color}`import(`./${mode}.css`)css.codeSplit: trueMiniCssExtractPlugin.linaria-xxxvanilla-extract.css.tscoldefineThemecompilePackage.cssinsertRulecreateTextNodeinjectStylesstyled-components/src/constructors/injectStyles.ts@emotion/sheet/src/index.jsinsertperformance.mark('mount-start')performance.mark('mount-end')