现代CSS-CSS-in-JS方案
CSS-in-JS是一种将样式直接写在JavaScript中的技术方案,它打破了传统的样式与逻辑分离的模式,为组件化开发带来了新的可能性。
本文将深入介绍主流的CSS-in-JS方案,包括styled-components、emotion、CSS Modules等,帮助你了解它们的特点、使用方式和适用场景,做出合适的技术选型。
# 一、CSS-in-JS概述
# 1.1 什么是CSS-in-JS
CSS-in-JS是一种在JavaScript代码中编写CSS样式的技术方案。它将样式与组件紧密结合,使样式成为组件的一部分。
传统方式:
// Button.jsx
import './Button.css';
function Button({ children }) {
return <button className="button">{children}</button>;
}
// Button.css
.button {
background: #667eea;
color: white;
padding: 10px 20px;
}
CSS-in-JS方式:
import styled from 'styled-components';
const Button = styled.button`
background: #667eea;
color: white;
padding: 10px 20px;
`;
function App() {
return <Button>Click me</Button>;
}
# 1.2 为什么需要CSS-in-JS
传统CSS的痛点:
- 全局命名空间:容易产生样式冲突
- 依赖管理困难:难以确定哪些样式可以安全删除
- 缺少隔离性:组件样式容易被外部影响
- 无法共享变量:CSS与JS之间难以共享状态
- 代码分割困难:样式难以按需加载
CSS-in-JS的优势:
✅ 作用域隔离:自动生成唯一类名,避免冲突 ✅ 动态样式:根据props或state动态生成样式 ✅ 死代码消除:未使用的样式自动删除 ✅ 类型安全:TypeScript支持完善 ✅ 代码分割:与组件一起按需加载 ✅ 服务端渲染:良好的SSR支持 ✅ 开发体验:热更新、自动前缀等
CSS-in-JS的劣势:
❌ 运行时开销:动态生成样式有性能成本 ❌ 包体积增加:需要引入额外的库 ❌ 学习成本:需要学习新的API ❌ 调试困难:生成的类名不直观 ❌ 首屏性能:可能影响首次渲染速度
# 1.3 主流方案对比
| 方案 | 类型 | 运行时 | 性能 | 流行度 |
|---|---|---|---|---|
| styled-components | 运行时 | 有 | 中等 | ⭐⭐⭐⭐⭐ |
| emotion | 运行时 | 有 | 较好 | ⭐⭐⭐⭐ |
| CSS Modules | 编译时 | 无 | 优秀 | ⭐⭐⭐⭐⭐ |
| JSS | 运行时 | 有 | 中等 | ⭐⭐⭐ |
| Linaria | 零运行时 | 无 | 优秀 | ⭐⭐⭐ |
| vanilla-extract | 零运行时 | 无 | 优秀 | ⭐⭐⭐ |
# 二、styled-components详解
# 2.1 styled-components简介
styled-components是最流行的CSS-in-JS库,使用ES6的标签模板字符串语法编写样式。
安装:
npm install styled-components
# 或
yarn add styled-components
# 2.2 基础使用
# 创建样式组件
import styled from 'styled-components';
// 基础样式组件
const Button = styled.button`
background: #667eea;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
&:hover {
background: #5568d3;
}
&:active {
transform: scale(0.98);
}
`;
// 使用
function App() {
return <Button>Click me</Button>;
}
# 基于props的样式
// 根据props动态改变样式
const Button = styled.button`
background: ${props => props.primary ? '#667eea' : '#ecf0f1'};
color: ${props => props.primary ? 'white' : '#2c3e50'};
padding: ${props => props.size === 'large' ? '15px 30px' : '10px 20px'};
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
opacity: 0.9;
}
`;
// 使用
function App() {
return (
<>
<Button primary>Primary Button</Button>
<Button>Default Button</Button>
<Button primary size="large">Large Button</Button>
</>
);
}
# 扩展样式
// 基础按钮
const Button = styled.button`
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
`;
// 扩展基础按钮
const PrimaryButton = styled(Button)`
background: #667eea;
color: white;
`;
const DangerButton = styled(Button)`
background: #e74c3c;
color: white;
`;
// 扩展React组件
const Link = ({ className, children, ...props }) => (
<a className={className} {...props}>
{children}
</a>
);
const StyledLink = styled(Link)`
color: #667eea;
text-decoration: none;
&:hover {
text-decoration: underline;
}
`;
# 传递props
// as属性:动态改变渲染元素
const Button = styled.button`
background: #667eea;
color: white;
padding: 10px 20px;
`;
function App() {
return (
<>
<Button>Button</Button>
<Button as="a" href="/">Link styled as Button</Button>
<Button as={Link} to="/">React Router Link</Button>
</>
);
}
// attrs:附加HTML属性
const Input = styled.input.attrs(props => ({
type: props.type || 'text',
placeholder: props.placeholder || 'Enter text...',
}))`
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
&:focus {
outline: none;
border-color: #667eea;
}
`;
# 2.3 高级特性
# 主题(Theme)
import { ThemeProvider } from 'styled-components';
// 定义主题
const lightTheme = {
colors: {
primary: '#667eea',
secondary: '#764ba2',
background: '#ffffff',
text: '#2c3e50',
},
spacing: {
small: '8px',
medium: '16px',
large: '24px',
},
};
const darkTheme = {
colors: {
primary: '#8891f2',
secondary: '#a78dd4',
background: '#2c3e50',
text: '#ffffff',
},
spacing: {
small: '8px',
medium: '16px',
large: '24px',
},
};
// 使用主题
const Button = styled.button`
background: ${props => props.theme.colors.primary};
color: white;
padding: ${props => props.theme.spacing.medium};
border-radius: 4px;
`;
const Container = styled.div`
background: ${props => props.theme.colors.background};
color: ${props => props.theme.colors.text};
padding: ${props => props.theme.spacing.large};
`;
// 应用主题
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeProvider theme={theme === 'light' ? lightTheme : darkTheme}>
<Container>
<Button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</Button>
</Container>
</ThemeProvider>
);
}
# 全局样式
import { createGlobalStyle } from 'styled-components';
const GlobalStyle = createGlobalStyle`
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
background: ${props => props.theme.colors.background};
color: ${props => props.theme.colors.text};
line-height: 1.6;
}
a {
color: ${props => props.theme.colors.primary};
text-decoration: none;
}
`;
function App() {
return (
<ThemeProvider theme={lightTheme}>
<GlobalStyle />
<Container>
{/* 应用内容 */}
</Container>
</ThemeProvider>
);
}
# CSS辅助函数
import styled, { css } from 'styled-components';
// 复用样式片段
const sharedStyles = css`
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
`;
const Button = styled.button`
${sharedStyles}
background: #667eea;
color: white;
`;
const Link = styled.a`
${sharedStyles}
background: transparent;
color: #667eea;
border: 1px solid #667eea;
`;
// 条件样式
const Button = styled.button`
background: #667eea;
color: white;
${props => props.disabled && css`
opacity: 0.6;
cursor: not-allowed;
`}
${props => props.size === 'large' && css`
padding: 15px 30px;
font-size: 18px;
`}
`;
# 动画
import styled, { keyframes } from 'styled-components';
// 定义动画
const fadeIn = keyframes`
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
`;
const rotate = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`;
// 使用动画
const FadeInBox = styled.div`
animation: ${fadeIn} 0.5s ease-in;
`;
const Spinner = styled.div`
width: 50px;
height: 50px;
border: 4px solid rgba(102, 126, 234, 0.3);
border-top-color: #667eea;
border-radius: 50%;
animation: ${rotate} 1s linear infinite;
`;
# 2.4 TypeScript支持
import styled from 'styled-components';
// 定义props类型
interface ButtonProps {
primary?: boolean;
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
}
const Button = styled.button<ButtonProps>`
background: ${props => props.primary ? '#667eea' : '#ecf0f1'};
color: ${props => props.primary ? 'white' : '#2c3e50'};
padding: ${props => {
switch (props.size) {
case 'small': return '8px 16px';
case 'large': return '15px 30px';
default: return '10px 20px';
}
}};
opacity: ${props => props.disabled ? 0.6 : 1};
cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};
`;
// 主题类型
interface Theme {
colors: {
primary: string;
secondary: string;
background: string;
text: string;
};
spacing: {
small: string;
medium: string;
large: string;
};
}
// 扩展styled-components的类型
declare module 'styled-components' {
export interface DefaultTheme extends Theme {}
}
# 三、emotion详解
# 3.1 emotion简介
emotion是一个高性能的CSS-in-JS库,提供了两种使用方式:@emotion/react(框架无关)和@emotion/styled(类似styled-components)。
安装:
npm install @emotion/react @emotion/styled
# 或
yarn add @emotion/react @emotion/styled
# 3.2 css属性方式
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
// 使用css prop
function Button() {
return (
<button
css={css`
background: #667eea;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
background: #5568d3;
}
`}
>
Click me
</button>
);
}
// 对象样式
function Button() {
return (
<button
css={{
background: '#667eea',
color: 'white',
padding: '10px 20px',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
'&:hover': {
background: '#5568d3',
},
}}
>
Click me
</button>
);
}
// 组合样式
const baseStyles = css`
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
`;
const primaryStyles = css`
background: #667eea;
color: white;
`;
function Button() {
return (
<button css={[baseStyles, primaryStyles]}>
Click me
</button>
);
}
# 3.3 styled方式
import styled from '@emotion/styled';
// 基础用法(与styled-components相同)
const Button = styled.button`
background: #667eea;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
background: #5568d3;
}
`;
// 对象样式
const Button = styled.button({
background: '#667eea',
color: 'white',
padding: '10px 20px',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
'&:hover': {
background: '#5568d3',
},
});
// 动态样式
const Button = styled.button`
background: ${props => props.primary ? '#667eea' : '#ecf0f1'};
color: ${props => props.primary ? 'white' : '#2c3e50'};
padding: 10px 20px;
`;
# 3.4 emotion特色功能
# 组合样式
import { css } from '@emotion/react';
const baseStyles = css`
padding: 10px 20px;
border-radius: 4px;
`;
const primaryStyles = css`
${baseStyles}
background: #667eea;
color: white;
`;
const dangerStyles = css`
${baseStyles}
background: #e74c3c;
color: white;
`;
# 主题
import { ThemeProvider } from '@emotion/react';
const theme = {
colors: {
primary: '#667eea',
secondary: '#764ba2',
},
};
function Button() {
return (
<button
css={theme => ({
background: theme.colors.primary,
color: 'white',
padding: '10px 20px',
})}
>
Click me
</button>
);
}
function App() {
return (
<ThemeProvider theme={theme}>
<Button />
</ThemeProvider>
);
}
# 全局样式
import { Global, css } from '@emotion/react';
function App() {
return (
<>
<Global
styles={css`
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
line-height: 1.6;
}
`}
/>
{/* 应用内容 */}
</>
);
}
# 3.5 emotion vs styled-components
| 特性 | emotion | styled-components |
|---|---|---|
| 包大小 | 更小 (7.9kB) | 较大 (15.5kB) |
| 性能 | 更快 | 较快 |
| API | 更灵活 | 更简单 |
| css prop | ✅ 原生支持 | ❌ 需要插件 |
| 对象样式 | ✅ 完善支持 | ⚠️ 部分支持 |
| SSR | ✅ 优秀 | ✅ 良好 |
| 社区 | 较大 | 最大 |
| TypeScript | ✅ 优秀 | ✅ 优秀 |
选择建议:
- emotion:追求性能和灵活性
- styled-components:追求稳定性和社区支持
# 四、CSS Modules详解
# 4.1 CSS Modules简介
CSS Modules是一种编译时的CSS-in-JS方案,通过构建工具自动生成唯一的类名,实现样式隔离。
特点:
- 零运行时开销
- 完全的CSS语法
- 自动作用域隔离
- 支持CSS预处理器
# 4.2 基础使用
/* Button.module.css */
.button {
background: #667eea;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.button:hover {
background: #5568d3;
}
.primary {
background: #667eea;
}
.secondary {
background: #764ba2;
}
.large {
padding: 15px 30px;
font-size: 18px;
}
// Button.jsx
import styles from './Button.module.css';
function Button({ primary, large, children }) {
return (
<button
className={`
${styles.button}
${primary ? styles.primary : ''}
${large ? styles.large : ''}
`}
>
{children}
</button>
);
}
// 使用classnames库简化
import classNames from 'classnames';
function Button({ primary, large, children }) {
return (
<button
className={classNames(
styles.button,
{ [styles.primary]: primary },
{ [styles.large]: large }
)}
>
{children}
</button>
);
}
# 4.3 组合样式
/* common.module.css */
.button {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
/* Button.module.css */
.primary {
composes: button from './common.module.css';
background: #667eea;
color: white;
}
.secondary {
composes: button from './common.module.css';
background: #764ba2;
color: white;
}
/* 组合多个类 */
.dangerButton {
composes: button large from './common.module.css';
background: #e74c3c;
color: white;
}
# 4.4 全局样式
/* 全局样式 */
:global(.app-container) {
max-width: 1200px;
margin: 0 auto;
}
/* 局部和全局混合 */
.wrapper :global(.external-class) {
color: red;
}
/* 切换到全局模式 */
:global {
.reset * {
margin: 0;
padding: 0;
}
}
# 4.5 配置
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.module\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: {
localIdentName: '[name]__[local]--[hash:base64:5]',
},
},
},
],
},
],
},
};
// vite.config.js
export default {
css: {
modules: {
localsConvention: 'camelCase',
generateScopedName: '[name]__[local]--[hash:base64:5]',
},
},
};
# 五、其他方案简介
# 5.1 JSS
import { createUseStyles } from 'react-jss';
const useStyles = createUseStyles({
button: {
background: '#667eea',
color: 'white',
padding: '10px 20px',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
'&:hover': {
background: '#5568d3',
},
},
primary: {
background: props => props.primary ? '#667eea' : '#ecf0f1',
},
});
function Button(props) {
const classes = useStyles(props);
return (
<button className={classes.button}>
{props.children}
</button>
);
}
# 5.2 Linaria
零运行时的CSS-in-JS方案,在构建时提取CSS:
import { css } from '@linaria/core';
import { styled } from '@linaria/react';
// css函数
const title = css`
font-size: 24px;
color: #2c3e50;
`;
// styled API
const Button = styled.button`
background: #667eea;
color: white;
padding: 10px 20px;
border-radius: 4px;
`;
function App() {
return (
<>
<h1 className={title}>Hello</h1>
<Button>Click me</Button>
</>
);
}
# 5.3 vanilla-extract
类型安全的零运行时CSS-in-JS:
// styles.css.ts
import { style } from '@vanilla-extract/css';
export const button = style({
background: '#667eea',
color: 'white',
padding: '10px 20px',
borderRadius: '4px',
selectors: {
'&:hover': {
background: '#5568d3',
},
},
});
// Button.tsx
import * as styles from './styles.css';
export function Button() {
return (
<button className={styles.button}>
Click me
</button>
);
}
# 六、方案选择指南
# 6.1 性能对比
| 方案 | 运行时 | 包大小 | 首屏性能 | 运行性能 |
|---|---|---|---|---|
| CSS Modules | 无 | 0KB | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Linaria | 无 | ~5KB | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| vanilla-extract | 无 | ~5KB | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| emotion | 有 | ~8KB | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| styled-components | 有 | ~16KB | ⭐⭐⭐ | ⭐⭐⭐ |
| JSS | 有 | ~15KB | ⭐⭐⭐ | ⭐⭐⭐ |
# 6.2 功能对比
| 功能 | CSS Modules | emotion | styled-components |
|---|---|---|---|
| 作用域隔离 | ✅ | ✅ | ✅ |
| 动态样式 | ❌ | ✅ | ✅ |
| 主题支持 | ⚠️ 需额外配置 | ✅ | ✅ |
| SSR | ✅ | ✅ | ✅ |
| 代码分割 | ✅ | ✅ | ✅ |
| TypeScript | ✅ | ✅ | ✅ |
| 开发体验 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 学习成本 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
# 6.3 选择建议
选择CSS Modules:
- ✅ 追求极致性能
- ✅ 不需要动态样式
- ✅ 团队熟悉传统CSS
- ✅ 需要使用CSS预处理器
选择emotion:
- ✅ 需要动态样式
- ✅ 追求灵活性
- ✅ 看重性能和包大小
- ✅ 需要css prop功能
选择styled-components:
- ✅ 需要动态样式
- ✅ 追求开发体验
- ✅ 看重社区和生态
- ✅ 团队已有经验
选择零运行时方案(Linaria/vanilla-extract):
- ✅ 追求零运行时开销
- ✅ 需要动态样式
- ✅ 愿意接受构建配置
# 6.4 决策流程
需要动态样式吗?
├─ 否 → CSS Modules
└─ 是 → 继续
性能是否关键?
├─ 是 → Linaria 或 vanilla-extract
└─ 否 → 继续
更看重什么?
├─ 性能和灵活性 → emotion
├─ 社区和稳定性 → styled-components
└─ 类型安全 → vanilla-extract
# 七、最佳实践
# 7.1 性能优化
# 避免动态样式
// ❌ 避免:每次渲染都创建新样式
function Button() {
return (
<button
css={css`
background: #667eea;
padding: 10px 20px;
`}
>
Click
</button>
);
}
// ✅ 推荐:提取到组件外部
const buttonStyles = css`
background: #667eea;
padding: 10px 20px;
`;
function Button() {
return <button css={buttonStyles}>Click</button>;
}
# 使用对象样式
// 对象样式有更好的缓存
const buttonStyles = {
background: '#667eea',
padding: '10px 20px',
borderRadius: '4px',
};
function Button() {
return <button css={buttonStyles}>Click</button>;
}
# 条件样式优化
// ❌ 避免:内联条件
<button css={{ background: isActive ? 'blue' : 'gray' }}>
// ✅ 推荐:预定义样式
const baseStyle = { padding: '10px' };
const activeStyle = { background: 'blue' };
const inactiveStyle = { background: 'gray' };
<button css={[baseStyle, isActive ? activeStyle : inactiveStyle]}>
# 7.2 代码组织
src/
├── styles/
│ ├── theme.ts # 主题配置
│ ├── global.ts # 全局样式
│ ├── mixins.ts # 样式混合
│ └── tokens.ts # 设计令牌
├── components/
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.styles.ts # 样式文件
│ │ └── index.ts
│ └── Card/
│ ├── Card.tsx
│ └── Card.styles.ts
# 7.3 主题设计
// theme.ts
export const theme = {
colors: {
primary: {
main: '#667eea',
light: '#8891f2',
dark: '#5568d3',
contrastText: '#ffffff',
},
secondary: {
main: '#764ba2',
light: '#a78dd4',
dark: '#5a3877',
contrastText: '#ffffff',
},
background: {
default: '#ffffff',
paper: '#f5f5f5',
},
text: {
primary: '#2c3e50',
secondary: '#7f8c8d',
disabled: '#bdc3c7',
},
},
spacing: (factor: number) => `${8 * factor}px`,
typography: {
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
fontSize: {
small: '12px',
medium: '14px',
large: '16px',
xlarge: '20px',
},
fontWeight: {
light: 300,
regular: 400,
medium: 500,
bold: 700,
},
},
shadows: {
small: '0 2px 4px rgba(0,0,0,0.1)',
medium: '0 4px 8px rgba(0,0,0,0.1)',
large: '0 8px 16px rgba(0,0,0,0.1)',
},
borderRadius: {
small: '4px',
medium: '8px',
large: '12px',
round: '50%',
},
transitions: {
duration: {
shortest: 150,
shorter: 200,
short: 250,
standard: 300,
complex: 375,
},
easing: {
easeInOut: 'cubic-bezier(0.4, 0, 0.2, 1)',
easeOut: 'cubic-bezier(0.0, 0, 0.2, 1)',
easeIn: 'cubic-bezier(0.4, 0, 1, 1)',
sharp: 'cubic-bezier(0.4, 0, 0.6, 1)',
},
},
breakpoints: {
xs: '0px',
sm: '576px',
md: '768px',
lg: '992px',
xl: '1200px',
},
};
export type Theme = typeof theme;
# 7.4 样式复用
// mixins.ts
import { css } from '@emotion/react';
export const flexCenter = css`
display: flex;
justify-content: center;
align-items: center;
`;
export const truncateText = css`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;
export const hideScrollbar = css`
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
`;
export const visuallyHidden = css`
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
`;
# 7.5 TypeScript最佳实践
// 定义组件props
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'text';
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
fullWidth?: boolean;
children: React.ReactNode;
}
// 定义样式props
interface StyledButtonProps {
$variant: ButtonProps['variant'];
$size: ButtonProps['size'];
$disabled: boolean;
$fullWidth: boolean;
}
// 使用$前缀避免props传递到DOM
const StyledButton = styled.button<StyledButtonProps>`
background: ${props => {
switch (props.$variant) {
case 'primary': return props.theme.colors.primary.main;
case 'secondary': return props.theme.colors.secondary.main;
default: return 'transparent';
}
}};
padding: ${props => {
switch (props.$size) {
case 'small': return props.theme.spacing(1);
case 'large': return props.theme.spacing(3);
default: return props.theme.spacing(2);
}
}};
width: ${props => props.$fullWidth ? '100%' : 'auto'};
opacity: ${props => props.$disabled ? 0.6 : 1};
cursor: ${props => props.$disabled ? 'not-allowed' : 'pointer'};
`;
export function Button({
variant = 'primary',
size = 'medium',
disabled = false,
fullWidth = false,
children,
}: ButtonProps) {
return (
<StyledButton
$variant={variant}
$size={size}
$disabled={disabled}
$fullWidth={fullWidth}
disabled={disabled}
>
{children}
</StyledButton>
);
}
# 八、总结
CSS-in-JS为现代Web开发提供了新的样式解决方案:
主流方案特点:
- styled-components:最成熟、社区最大、开发体验最好
- emotion:性能更好、更灵活、包体积更小
- CSS Modules:零运行时、性能最优、学习成本低
- 零运行时方案:结合两者优势,但配置较复杂
选择建议:
- 追求性能 → CSS Modules 或零运行时方案
- 需要动态样式 → emotion 或 styled-components
- 大型项目 → styled-components(生态成熟)
- 中小型项目 → emotion(灵活轻量)
- 简单项目 → CSS Modules(简单直接)
最佳实践:
- 合理组织代码结构
- 提取样式到组件外部
- 使用主题系统
- 复用样式片段
- 注意性能优化
- 善用TypeScript
无论选择哪种方案,都要:
- 理解其原理和特点
- 遵循最佳实践
- 注重性能和可维护性
- 根据项目实际需求选择
祝你变得更强!