styled-components和@emotion/react在高频交互时卡顿,根源在于运行时反复拼字符串、算哈希、插入style标签;zero-runtime方案如vanilla-extract或linaria将样式生成移至构建阶段,彻底消除insertrule等运行时操作。

styled-components 和 @emotion/react 在滚动、列表重渲染或高频交互时卡顿,不是你组件写得不好,而是它们默认在主线程里反复做三件事:拼样式字符串、计算哈希、插入 <style></style> 标签。这事没法靠加 React.memo 或节流解决——根源在 runtime。
用 zero-runtime 工具替代 runtime 注入
把样式生成从浏览器搬到构建阶段,彻底砍掉 insertRule、createTextNode 这类操作。
-
vanilla-extract适合 TypeScript 项目:样式写在.css.ts文件里,build 时直接输出.css文件,不带任何运行时依赖 -
linaria更贴近传统写法:支持在 JSX 里写css模板字符串,但要求所有值必须是编译期可确定的(不能是props.color或闭包) - 两者都不支持动态 import 路径(如
import(`./${theme}.css`)),否则构建失败 - Vite 用户要检查
build.cssCodeSplit是否开启;Webpack 用户需确认mini-css-extract-plugin或类似插件已启用
验证 runtime 开销是否真被移除
别只看打包体积变小了——那可能是 tree-shaking 的功劳,和 CSS-in-JS 无关。
- 打开 Chrome DevTools → Performance → 录制一次滚动或切换操作 → 搜索
insertRule或style.textContent,结果应几乎为零 - 在
styled-components的injectStyles或@emotion/sheet的insert方法上打断点,确认不再触发 - 用
performance.mark()包住组件挂载过程,对比前后 commit 阶段中 style 相关子任务是否消失
如果必须保留 styled-components/emotion,至少做这三件事
不是所有项目都能立刻迁移到 zero-runtime,临时缓解也得讲方法。
- 对高频组件(如
Table行、Card列表项)用React.memo+ 缓存样式对象(例如 Ant Design 的useStyleCache),避免每次渲染都调用css函数 - 禁用 emotion 的
speedy模式(设为false)——它在某些低端安卓 WebView 下反而引发重绘抖动 - 避免在样式函数里读取 props 中的深层嵌套对象或执行复杂计算,改用预处理后的扁平字段(如
props.hasError而非props.meta?.error?.code)
css 函数在 render 里反复跑。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











