CSS-in-JS 的维护收益源于强制暴露样式与组件的耦合关系,需通过收口全局样式、函数化动态逻辑、避免副作用、配合Tree-shaking等机制约束,否则易退化为更难调试的混合体。

styled-components 和 @emotion/react 这类库在大型项目中确实能降低维护成本,但前提是团队已建立配套约束——不是“用了就自动变好”,而是靠机制把动态性管住。
组件样式必须随组件一起删除,否则会残留
传统 CSS 文件常被遗忘:组件删了,button.css 还躺在目录里;而 styled.button 写在 Button.jsx 里,删文件即清样式。但要注意:createGlobalStyle 或主题对象(如 theme.js)若散落在多处,反而变成新债。建议把全局样式、主题变量、设计令牌全部收口到 src/theme/ 下,且禁止跨目录 import 样式片段。
动态样式别写成“if-else 堆砌”,要用函数抽象
常见错误是这样写:
const Button = styled.button`
background: ${props => {
if (props.variant === 'primary') return '#007bff';
if (props.variant === 'danger') return '#dc3545';
if (props.size === 'lg') return '#28a745';
return '#6c757d';
}};
`
这种写法难测试、难复用、难改。应拆成独立函数:
const getBgColor = ({ variant, size }) => {
if (variant) return variantMap[variant] || '#6c757d';
if (size === 'lg') return '#28a745';
return '#6c757d';
};
然后在模板字符串里调用:background: ${getBgColor};
立即学习“前端免费学习笔记(深入)”;
- 函数可单独单元测试
- 支持 TypeScript 类型推导(
variantMap可定义为Record<string, string>) - 避免 props 混合判断逻辑污染样式定义
不要在 styled 组件里直接读 context 或 useTheme,除非你明确承担副作用
比如这样写:
const StyledButton = styled.button`
color: ${() => useTheme().text.primary};
`
会导致该组件无法被 React.memo 缓存,每次父组件 rerender 都强制重算样式。正确做法是让父组件把 theme 值传下来:
const Button = ({ theme, ...props }) => (
<StyledButton theme={theme} {...props} />
);
const StyledButton = styled.button`
color: ${props => props.theme.text.primary};
`
或者用 css prop + useTheme 组合,但必须配合 React.memo 包裹组件。
Tree-shaking 不是自动的,得靠写法配合
@emotion/react 支持按需引入,但如果你写成:
import { css } from '@emotion/react';
const style = css`...`; // ❌ 全局变量,无法被摇掉
换成函数式写法才能被识别:
const Button = ({ children }) => (
<button css={theme => ({ color: theme.text.primary })}>
{children}
</button>
); // ✅ 可被 webpack / esbuild 摇掉未使用的 theme 字段
同理,styled 创建的组件名必须是 const 声明,不能是 let 或 var,否则打包工具无法静态分析依赖。


















