CSS-in-JS易出问题因内联样式不支持伪类、媒体查询等;真正方案需运行时生成唯一class并注入style标签;styled-components与emotion在SSR、主题、TS支持及性能上各有侧重。

为什么直接在JS里写CSS容易出问题
CSS-in-JS不是简单地把style对象塞进React.createElement或style属性里。那样只是内联样式(inline style),不支持伪类、媒体查询、全局作用域复用,也无法利用CSS的层叠和继承机制。
常见错误现象包括:
-
:hover、::before完全失效 - 响应式断点只能靠JS监听
window.innerWidth手动切换class - 同一组样式在多个组件中复制粘贴,改一处漏十处
- 样式污染:一个组件的
.button意外覆盖了另一个组件的同名class
真正可用的CSS-in-JS方案,核心是运行时生成唯一class名 + 注入真实标签,而不是拼字符串或只靠style属性。
styled-components 和 emotion 的关键区别在哪
两者都通过模板字符串解析CSS并注入,但底层行为不同:
立即学习“Java免费学习笔记(深入)”;
-
styled-components默认启用服务端渲染(SSR)兼容模式,会为每个组件生成带哈希的class(如sc-bdVaJa),且支持withConfig控制是否启用cssprop -
@emotion/react更轻量,css函数返回的是class名字符串,可直接用于className;styledAPI行为类似,但支持更灵活的主题透传(比如用useTheme读取嵌套theme值)
使用场景差异:
- 如果项目已用
create-react-app且不想配Babel插件,优先选styled-components(开箱即用) - 如果需要细粒度控制样式注入时机(比如只在某个模块加载后才注入),
@emotion/react的cache配置更直接 -
emotion对TypeScript的类型提示更完善,尤其是css函数的属性自动补全
性能影响:两者都会做样式去重,但styled-components在严格模式下会对props做深比较,可能引发不必要的重渲染;emotion默认浅比较,更可控。
如何避免CSS-in-JS导致的重复注入和内存泄漏
这不是理论风险,而是真实发生过的线上问题:同一个组件被多次动态导入(比如React.lazy + Suspense),每次加载都重新执行styled.div,导致相同样式被反复插入标签。
关键对策:
- 确保
styled.xxx定义写在模块顶层,不要放在组件函数体内或useEffect里 - 使用
babel-plugin-styled-components(对应styled-components)或@emotion/babel-plugin,开启pure模式,让Babel在编译期剥离无副作用的样式定义 - 在服务端渲染时,统一用
ServerStyleSheet(styled-components)或CacheProvider+renderStylesToString(emotion)收集所有样式,避免客户端重复计算
一个典型错误写法:
function MyButton() {
const StyledDiv = styled.div`color: red`; // ❌ 每次渲染都新建,class名不复用
return Click ;
}要不要用 vanilla-extract 或 Linaria 替代 runtime 方案
vanilla-extract和Linaria属于zero-runtime CSS-in-JS,它们在构建时(build time)就把JS中的样式提取成真实CSS文件,最终产物里没有样式解析逻辑。
适用条件很明确:
- 项目用
Vite或Webpack 5+,能接入自定义loader(vanilla-extract需@vanilla-extract/webpack-plugin) - 不依赖运行时主题切换(比如暗色模式需JS控制)、不依赖组件props动态生成样式(如
color={props.variant === 'danger' ? 'red' : 'blue'}) - 团队接受额外的学习成本:要写
export const sprinkles = createSprinkles(...)而不是直接写styled.div
容易被忽略的一点:这些方案无法支持基于DOM状态的样式,比如input:focus + label或div:has(> p),因为它们不操作真实DOM,也不生成运行时选择器逻辑。
真正的复杂点不在“怎么写”,而在于样式生命周期是否与组件一致——runtime方案天然绑定,build-time方案必须靠约定和工具链兜底。










