样式之战:2026 年的格局
前端样式领域在多年的碎片化后已趋于稳定。CSS-in-JS(styled-components、Emotion)、Tailwind CSS 和 CSS Modules 各有明确的适用场景。本文抛开炒作,基于真实的性能数据和开发体验权衡,为你提供决策框架。
三大竞争者
CSS-in-JS(Emotion、styled-components)
CSS-in-JS 在运行时(或通过静态提取在构建时)从 JavaScript 生成 CSS。它在 2018–2022 年间是主流范式。
// Emotion — 样式与组件共存
import { css } from '@emotion/react'
import styled from '@emotion/styled'
const buttonBase = css`
display: inline-flex;
align-items: center;
padding: 8px 16px;
border-radius: 6px;
font-weight: 500;
cursor: pointer;
`
const PrimaryButton = styled.button`
${buttonBase}
background: ${props => props.theme.colors.primary};
color: white;
&:hover {
background: ${props => props.theme.colors.primaryHover};
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
`
优势:
- 基于 props 的条件样式自然且类型安全
- 自动作用域隔离,无类名冲突
- 与 TypeScript 结合的主题系统符合人体工程学
- 样式与组件逻辑共存
劣势:
- 运行时开销:CSS 在渲染时解析并注入
- JavaScript 包体积较大(styled-components:约 16KB gzipped)
- 动态样式在每次渲染时都会重新计算
- 在 RSC(React Server Components)中性能较差——它们仅在客户端运行
零运行时 CSS-in-JS(Linaria、vanilla-extract、Panda CSS)
下一代演进:CSS-in-JS 语法,但在构建时静态提取:
// vanilla-extract — 类型安全的 CSS,零运行时
import { style, createTheme, styleVariants } from '@vanilla-extract/css'
export const [themeClass, vars] = createTheme({
color: {
primary: '#0070f3',
primaryHover: '#0051b5',
},
space: {
sm: '8px',
md: '16px',
},
})
export const button = style({
display: 'inline-flex',
alignItems: 'center',
padding: `${vars.space.sm} ${vars.space.md}`,
borderRadius: '6px',
fontWeight: 500,
cursor: 'pointer',
})
export const buttonVariants = styleVariants({
primary: {
background: vars.color.primary,
color: 'white',
':hover': { background: vars.color.primaryHover },
},
secondary: {
background: 'transparent',
border: `1px solid ${vars.color.primary}`,
color: vars.color.primary,
},
})
零运行时、完整的 TypeScript 推断和静态 CSS 输出。两全其美——但构建复杂度更高。
Tailwind CSS v4
Tailwind v4(2024 年发布)带来了重大变化:纯 CSS 配置(不再需要 tailwind.config.js)、原生 CSS 变量以及使用 Rust 编写的 Oxide 引擎带来的极速构建。
/* tailwind.css — v4 配置在 CSS 中 */
@import "tailwindcss";
@theme {
--color-primary: oklch(55% 0.2 265);
--color-primary-hover: oklch(45% 0.2 265);
--font-sans: 'Inter', system-ui, sans-serif;
--radius-md: 6px;
}
// 使用 Tailwind 工具类的组件
function Button({ variant = 'primary', children }: ButtonProps) {
return (
<button className={cn(
'inline-flex items-center px-4 py-2 rounded-md font-medium cursor-pointer',
'transition-colors duration-150',
variant === 'primary' && 'bg-primary text-white hover:bg-primary-hover',
variant === 'secondary' && 'bg-transparent border border-primary text-primary hover:bg-primary/10',
)}>
{children}
</button>
)
}
优势:
- 零运行时开销——生产环境为纯 CSS
- 极小的 CSS 包体积,完美的 tree shaking(仅使用到的工具类)
- 优秀的 IDE 支持(Tailwind IntelliSense)
- 无需离开 HTML 即可实现一致的设计系统
- v4 Oxide 引擎:构建速度提升 5 倍
劣势:
- 类名字符串冗长,尤其是包含多个条件样式时
- 逻辑与样式混合在 className prop 中
- 不常用工具类的学习曲线
- 重构时需要查找所有类名使用处
CSS Modules
CSS Modules 提供局部作用域的 CSS,且零 JavaScript 开销:
/* Button.module.css */
.button {
display: inline-flex;
align-items: center;
padding: 8px 16px;
border-radius: 6px;
font-weight: 500;
cursor: pointer;
}
.primary {
background: var(--color-primary);
color: white;
}
.primary:hover {
background: var(--color-primary-hover);
}
.secondary {
background: transparent;
border: 1px solid var(--color-primary);
color: var(--color-primary);
}
import styles from './Button.module.css'
import clsx from 'clsx'
function Button({ variant = 'primary', children }: ButtonProps) {
return (
<button className={clsx(styles.button, styles[variant])}>
{children}
</button>
)
}
优势:
- 零运行时,零包体积开销
- 标准 CSS——所有 CSS 特性均可使用
- 优秀的浏览器 DevTools 体验
- 适用于所有环境,包括 SSR/RSC
劣势:
- 没有内置的主题系统
- 难以与组件逻辑共存
- 动态样式需要 CSS 自定义属性或类名拼接
性能对比
| 方案 | JS 包体积 | CSS 包体积 | 运行时开销 | SSR/RSC | 开发体验 |
|---|---|---|---|---|---|
| styled-components | +16KB | 0(注入) | 高 | 有限 | 优秀 |
| Emotion | +11KB | 0(注入) | 中等 | 有限 | 优秀 |
| vanilla-extract | ~0KB | 提取 | 无 | 完整 | 良好 |
| Tailwind v4 | 0 | 极小 | 无 | 完整 | 优秀 |
| CSS Modules | 0 | 标准 | 无 | 完整 | 良好 |
真实包体积影响
在一个生产环境仪表盘应用(Next.js 15)上测量:
styled-components:
- JS:+16.3KB gzipped
- LCP:2.4s
- TBT:180ms(样式注入阻塞主线程)
vanilla-extract:
- JS:+0.5KB(SSR 水合运行时)
- LCP:1.9s
- TBT:40ms
Tailwind v4:
- JS:0KB
- CSS:12KB gzipped(仅使用到的工具类)
- LCP:1.7s
- TBT:20ms
决策框架
选择 CSS-in-JS(Emotion/styled-components)当:
- 团队拥有强大的 JavaScript 能力,偏好 JS 优先的开发方式
- 设计系统需要复杂的动态主题(暗色模式、用户可自定义主题)
- 开发纯客户端 SPA,SSR 不是问题
- 已在项目中大量使用,迁移成本超过收益
选择零运行时 CSS-in-JS(vanilla-extract、Panda CSS)当:
- 希望获得 CSS-in-JS 的开发体验且零性能成本
- TypeScript 优先的设计系统是首要目标
- 团队能接受额外的构建工具复杂度
选择 Tailwind v4 当:
- 启动新项目——它现在是务实默认选择
- 团队包含设计师和开发者(工具类可读性强)
- 性能和包体积是优先考虑因素
- 使用 React Server Components 或其他 SSR 密集型架构
选择 CSS Modules 当:
- 团队偏好编写真正的 CSS
- 需要集成现有的大型 CSS 代码库
- 需要对输出 CSS 有最大控制权
- 在非 React 环境中工作
Tailwind v4 迁移说明
/* v3:JavaScript 配置 */
/* tailwind.config.js: theme: { extend: { colors: { primary: '#0070f3' } } } */
/* v4:CSS 配置 */
@theme {
--color-primary: #0070f3;
}
/* v3 任意值仍然有效 */
.element {
@apply text-[14px] leading-[1.6];
}
/* v4:新的 oklch 色彩空间支持 */
@theme {
--color-brand: oklch(65% 0.18 250);
}
结论
在 2026 年,Tailwind v4 是新项目的务实默认选择。其 v4 重新设计解决了主要批评:配置现在在 CSS 中,构建速度前所未有地快,设计令牌系统也是一流的。
对于具有复杂主题需求的设计系统,vanilla-extract 提供了最佳的 TypeScript 集成且零运行时成本。老牌 CSS-in-JS(styled-components、Emotion)在零运行时替代方案出现并拥有可比的开发体验后,已失去性能优势。
CSS Modules 仍然是那些偏好编写真正 CSS 并希望最大简单性的团队的绝佳选择。如有疑问,选择你的团队会乐于长期维护的方案。