人人都会AI编程

Emotion:高性能 CSS-in-JS 方案

更新时间:2026-07-11

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 需要配置缓存键和样式提取,避免样式闪烁。通常做法:

  1. 创建 Emotion 缓存实例。
  2. _document 或根组件中注入 CacheProvider
  3. 使用 @emotion/server 提供的 extractCriticalrenderStylesToString 提取样式。

具体配置可参考 Emotion 官方文档,与 Next.js 集成非常简单。

Emotion vs styled-components 选型对比

| 特性 | Emotion | styled-components |
|------|---------|-------------------|
| API 风格 | css prop + styled | 主要 styled |
| 包体积 | 更小(约 10KB gziped) | 略大 |
| 性能(运行时) | 稍快,缓存更精细 | 优秀,但稍重 |
| TypeScript 支持 | 优秀 | 优秀 |
| 组合性 | 非常灵活,数组/对象拼接 | 支持继承与组合 |
| 学习成本 | 中等 | 中等 |
| 社区与生态 | 大(与 MUI 深度集成) | 大,知名度更高 |

选型建议

  • 如果你需要极致的灵活性和性能,并且喜欢直接写 css prop,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 方案之一,特别适合追求高度定制化、动态样式的复杂应用。