Emotion 是什么
Emotion 是一个高性能、灵活且轻量的 CSS-in-JS 库,允许你在 JavaScript 或 TypeScript 中编写样式,并直接应用到 React 组件上。它的设计目标是提供出色的运行时性能,同时保持 API 的简洁和强大的组合能力。
与 styled-components 类似,Emotion 支持两种主要的使用方式:styled 组件模式和css prop 模式,开发者可以根据场景灵活选择。
核心优势
- 运行时性能高:Emotion 通过缓存和最小化样式注入,避免了不必要的样式重新计算,在大量组件渲染时性能优于多数同类库。
- 无额外 Provider:不需要像某些方案那样在根组件包裹 Provider,开箱即用。
- 强大的组合性:样式可以像普通字符串或对象一样传递、继承和动态计算。
- 与 React 深度集成:专为 React 设计,支持 css prop 模式,写起来更接近原生 style 属性,学习成本更低。
- 服务端渲染支持:提供
@emotion/server工具,轻松实现 SSR 下的样式提取与注水。 - TypeScript 友好:完善的类型推断和高阶组件类型定义。
两种核心用法
1. css prop 模式(推荐用于快速内联样式)
在组件的 css 属性上直接编写样式字符串或对象,就像写内联 style,但它是真正的 CSS 类名生成。
import { css } from '@emotion/react';
function Button({ primary, children }) {
return (
<button
css={css`
padding: 12px 24px;
border-radius: 8px;
border: none;
background-color: ${primary ? '#2563eb' : '#e5e7eb'};
color: ${primary ? '#fff' : '#333'};
font-size: 16px;
cursor: pointer;
&:hover {
opacity: 0.9;
}
`}
>
{children}
</button>
);
}
也可以传入对象形式,便于 TypeScript 类型检查和动态计算:
const dangerStyle = css({
backgroundColor: '#dc2626',
color: '#fff',
'&:hover': {
backgroundColor: '#b91c1c',
},
});
<button css={dangerStyle}>删除</button>
2. styled 组件模式(用于创建可复用样式组件)
类似 styled-components,利用模板字符串定义样式,返回一个包含样式的 React 组件。
import styled from '@emotion/styled';
const Card = styled.div`
background: #fff;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.2s;
&:hover {
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
}
`;
const Title = styled.h2`
font-size: 20px;
margin-bottom: 12px;
color: #1e293b;
`;
使用方式与普通组件一致:
<Card>
<Title>项目状态</Title>
<p>一切正常</p>
</Card>
动态样式与 Props 传递
styled 组件可以接收 Props 并动态调整样式,极大提升了灵活性。
const Button = styled.button`
padding: 10px 20px;
border-radius: 8px;
border: none;
background-color: ${props => props.variant === 'outline' ? 'transparent' : '#2563eb'};
color: ${props => props.variant === 'outline' ? '#2563eb' : '#fff'};
border: ${props => props.variant === 'outline' ? '2px solid #2563eb' : 'none'};
cursor: pointer;
font-weight: 500;
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
`;
// 使用
<Button variant="outline">次要操作</Button>
<Button disabled>提交</Button>
全局样式
Emotion 提供 Global 组件,用于重置浏览器的默认样式或定义全局 CSS 变量。
import { Global, css } from '@emotion/react';
function App() {
return (
<>
<Global
styles={css`
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background: #f8fafc;
}
:root {
--primary: #2563eb;
--text: #1e293b;
}
`}
/>
<div>你的应用内容</div>
</>
);
}
组合与样式继承
多个样式块可以通过数组或组合函数合并,避免样式重复。
const base = css`
padding: 12px;
border-radius: 6px;
`;
const primary = css`
${base};
background-color: blue;
color: white;
`;
const danger = css`
${base};
background-color: red;
color: white;
`;
<button css={primary}>保存</button>
<button css={danger}>删除</button>
服务端渲染(SSR)支持
在 Next.js 或自定义 SSR 中,Emotion 需要配置缓存键和样式提取,避免样式闪烁。通常做法:
- 创建 Emotion 缓存实例。
- 在
_document或根组件中注入CacheProvider。 - 使用
@emotion/server提供的extractCritical或renderStylesToString提取样式。
具体配置可参考 Emotion 官方文档,与 Next.js 集成非常简单。
Emotion vs styled-components 选型对比
| 特性 | Emotion | styled-components |
|------|---------|-------------------|
| API 风格 | css prop + styled | 主要 styled |
| 包体积 | 更小(约 10KB gziped) | 略大 |
| 性能(运行时) | 稍快,缓存更精细 | 优秀,但稍重 |
| TypeScript 支持 | 优秀 | 优秀 |
| 组合性 | 非常灵活,数组/对象拼接 | 支持继承与组合 |
| 学习成本 | 中等 | 中等 |
| 社区与生态 | 大(与 MUI 深度集成) | 大,知名度更高 |
选型建议:
- 如果你需要极致的灵活性和性能,并且喜欢直接写
cssprop,Emotion 是更好的选择。 - 如果你更偏爱styled 模板字符串的经典写法,并且团队对 styled-components 更熟悉,两者都可以,但 Emotion 在相同场景下通常更轻更快。
- 如果你在 MUI (Material-UI) 或 Chakra UI 等组件库之上开发,它们已经内置 Emotion,无需额外引入。
注意事项
- Emotion 需要 Babel 插件(
@emotion/babel-plugin)来获得更好的开发体验(如 source map、组件名称、压缩优化)。使用 Vite 时,可以通过@emotion/babel-plugin或 Vite 的 React 插件自动处理。 - 避免在渲染内频繁创建新的样式对象(即使是在
css prop中),可以提取到组件外部,利用 Emotion 的缓存机制提升性能。 - CSS-in-JS 会在运行时生成样式标签,如果你的应用非常注重极致首屏性能,可以结合静态 CSS 提取工具(如使用
@emotion/cache与关键 CSS 提取),或考虑使用零运行时方案(如 Tailwind 或 Vanilla Extract)。
Emotion 凭借其性能和灵活性,已经成为 React 生态中最受欢迎的 CSS-in-JS 方案之一,特别适合追求高度定制化、动态样式的复杂应用。