直接迁移不可行,必须分阶段替换:先保留运行时能力支撑动态逻辑,再用 zero-runtime 工具抽离静态部分为 css;vanilla-extract 或 linaria 仅支持编译期确定的样式,如 token 变量、固定媒体查询等,不支持依赖 props、state 或运行时计算的样式。

直接迁移不可行,必须分阶段替换:先保留运行时能力支撑动态逻辑,再用 zero-runtime 工具把静态部分抽离为 CSS 文件。
哪些样式能提前抽成静态 CSS
vanilla-extract 或 linaria 只能处理编译期可确定的样式——也就是不依赖 props、state、闭包或运行时计算的部分。比如主题色变量、间距 token、基础字体大小、固定断点下的媒体查询规则。
- ✅ 可迁移:
color: tokens.primary(tokens是 const 对象)、@media (min-width: 768px)、border-radius: 4px - ❌ 不可迁移:
color: props.isActive ? 'blue' : 'gray'、width: `${props.width}px`、background: useTheme().bg - ⚠️ 容易误判:带函数调用但实际是纯计算的表达式(如
calc(100% - ${gap}px)),linaria 能处理;但若gap来自 import 的模块变量且该模块被 webpack 动态加载,则构建失败
如何识别并隔离动态样式逻辑
不是所有 styled 组件都需要重写。重点找那些真正依赖运行时状态的地方:主题切换器、表单验证反馈、动画触发控制、用户偏好适配(如 prefers-reduced-motion)。
- 用
React DevTools查看组件 re-render 频次,高频更新的组件优先保留 CSS-in-JS - 搜索项目中含
props.、useState、useContext直接参与样式的模板字符串或对象字面量 - 把动态部分拆成独立的
className+ CSS 类名切换,例如:className={clsx('btn', { 'btn--loading': loading })},然后用原生 CSS 实现.btn--loading - 避免在 vanilla-extract 的
style函数里写if (process.env.NODE_ENV === 'development')这类条件——它只认常量折叠,不执行 JS 运行时逻辑
构建链路怎么改才不破环现有流程
不能一刀切替换工具链。linaria 和 vanilla-extract 都需要 Babel 或 TypeScript 插件介入构建,但它们和 @emotion/react 或 styled-components 共存时,需明确边界。
- 给新业务模块默认启用
vanilla-extract,老模块继续用 emotion,通过目录约定隔离(如src/features/new/**/styles.css.ts) - 禁用 emotion 的
cssprop(jsxImportSource配置),改用显式className,否则无法被 static 工具识别 - linaria 要求所有样式文件后缀为
.css.ts或.css.js,且不能有import()动态导入——否则 Babel 插件无法静态分析 - SSR 场景下,vanilla-extract 生成的 CSS 必须通过
getStylesheet提前注入到head,而 emotion 的CacheProvider仍要保留用于剩余动态样式
真正的难点不在语法转换,而在判断“哪里真需要动态”——很多所谓“动态样式”其实只是开发初期图省事写的 props 映射,上线后根本没变过。先做一次样式使用审计,比盲目替换工具更有效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











