掘金文章归档 · 第 14 篇 React 系列 1/4 CSS-in-JS

React 样式——styled-components:从基础语法到实战场景

styled.xxx 原生标签 + styled(组件) 全场景适配,props 动态样式、样式继承、全局与主题管理

原文作者:随意_(掘金) | juejin.cn/post/7606128564414414875 | 发布于 2026-02-14 | 阅读 214 · 约 9 分钟

0什么是 styled-components?核心优势

在 React 开发中,样式管理一直是绕不开的核心问题——全局 CSS 命名冲突、动态样式繁琐、样式与组件解耦难等痛点,长期困扰着前端开发者。而 styled-components 作为 React 生态中最主流的 CSS-in-JS 方案,彻底颠覆了传统样式编写方式,将样式与组件深度绑定。

它由 Max Stoiber 于 2016 年推出,GitHub 星数超 40k,被 Airbnb、Netflix、Spotify 等大厂广泛采用。

核心优势说明
样式封装,杜绝污染每个样式组件生成唯一的 className,彻底解决全局 CSS 命名冲突问题
动态样式,灵活可控直接通过组件 props 控制样式,无需拼接 className 或写内联样式
自动前缀,兼容省心自动为 CSS 属性添加浏览器前缀(如 -webkit-、-moz-)
语义化强,易维护样式与组件代码同文件,逻辑闭环,可读性和可维护性大幅提升
按需打包,体积优化打包时自动移除未使用的样式,减少冗余代码
通用适配,场景全覆盖既支持 HTML 原生标签,也兼容自定义组件、第三方 UI 组件(KendoReact / AntD)

1基础语法:styled.原生标签 与 styled(组件)

1.1 安装

# npm
npm install styled-components

# yarn
yarn add styled-components

# TypeScript 类型声明(新版已内置,可选)
npm install @types/styled-components --save-dev

1.2 语法形式 1:styled.原生标签(快捷写法)

这是最常用的基础语法,styled. 后紧跟 HTML 原生标签名(div/button/p/h1/input 等),本质是 styled() 函数的语法糖。多标签示例:

import React from 'react';
import styled from 'styled-components';

// 1. 布局容器:div
const Container = styled.div`
  width: 90%;
  max-width: 1200px;
  margin: 20px auto;
  padding: 24px;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.05);
`;

// 2. 标题:h1/h2
const TitleH1 = styled.h1`
  color: #1f2937;
  font-size: 32px;
  font-weight: 700;
  margin-bottom: 16px;
`;

// 3. 文本:p/span
const Paragraph = styled.p`
  color: #4b5563;
  font-size: 16px;
  line-height: 1.6;
  margin-bottom: 12px;
`;
const HighlightText = styled.span`
  color: #2563eb;
  font-weight: 500;
`;

// 4. 交互:button/a
const PrimaryButton = styled.button`
  padding: 10px 20px;
  background-color: #2563eb;
  color: white;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  &:hover { background-color: #1d4ed8; }
  &:disabled { background-color: #93c5fd; cursor: not-allowed; }
`;
const Link = styled.a`
  color: #2563eb;
  text-decoration: none;
  &:hover { text-decoration: underline; color: #1d4ed8; }
`;

// 5. 表单:input/label
const FormLabel = styled.label`
  display: block;
  font-size: 14px;
  color: #374151;
  margin-bottom: 6px;
`;
const Input = styled.input`
  width: 100%;
  padding: 10px 12px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  &:focus {
    outline: none;
    border-color: #2563eb;
    box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2);
  }
`;

// 6. 列表:ul/li
const List = styled.ul`
  margin: 16px 0;
  padding-left: 24px;
`;
const ListItem = styled.li`
  margin-bottom: 8px;
  &:last-child { margin-bottom: 0; }
`;

// 使用示例
function BasicTagDemo() {
  return (
    <Container>
      <TitleH1>原生标签样式化示例</TitleH1>
      <Paragraph>
        这是 <HighlightText>styled.p</HighlightText> 渲染的段落,支持 <HighlightText>styled.span</HighlightText> 行内样式。
      </Paragraph>
      <List>
        <ListItem>styled.div:布局容器核心标签</ListItem>
        <ListItem>styled.button:交互按钮,支持 hover/禁用状态</ListItem>
        <ListItem>styled.input:表单输入框,支持焦点样式</ListItem>
      </List>
      <FormLabel htmlFor="username">用户名</FormLabel>
      <Input id="username" placeholder="请输入用户名" />
      <PrimaryButton style={{ marginTop: '10px' }}>提交</PrimaryButton>
      <Link href="#" style={{ marginLeft: '10px' }}>忘记密码?</Link>
    </Container>
  );
}

1.3 语法形式 2:styled(组件)(自定义 / 第三方组件适配)

当需要给自定义 React 组件第三方 UI 组件添加样式时,必须使用 styled() 通用函数(styled.xxx 仅支持原生标签)。核心要求:被包裹的组件需接收并传递 className 属性到根元素

示例 1:给自定义组件加样式

import React from 'react';
import styled from 'styled-components';

// 自定义组件:必须传递 className 到根元素
const MyButton = ({ children, className }) => {
  // 关键:将 className 传给根元素 <button>,样式才能生效
  return <button className={className}>{children}</button>;
};

// 用 styled() 包裹自定义组件,添加样式
const StyledMyButton = styled(MyButton)`
  background-color: #28a745;
  color: white;
  border: none;
  padding: 8px 16px;
  border-radius: 4px;
  &:hover { background-color: #218838; }
`;

function CustomComponentDemo() {
  return <StyledMyButton>自定义组件样式化</StyledMyButton>;
}

示例 2:给第三方组件(KendoReact)加样式

import React from 'react';
import styled from 'styled-components';
// 引入 KendoReact 按钮组件
import { Button } from '@progress/kendo-react-buttons';

// 用 styled() 覆盖第三方组件默认样式
const StyledKendoButton = styled(Button)`
  background-color: #dc3545 !important; /* 覆盖组件内置样式 */
  border-color: #dc3545 !important;
  color: white !important;
  padding: 8px 16px !important;

  &:hover {
    background-color: #c82333 !important;
  }
`;

function ThirdPartyComponentDemo() {
  return <StyledKendoButton>自定义样式的 KendoReact 按钮</StyledKendoButton>;
}
📌 两种语法关系:styled.xxxstyled('xxx') 的语法糖(如 styled.div === styled('div')),仅简化原生标签写法;styled(组件) 是通用方案,覆盖所有组件类型。

2进阶技巧:动态样式 / 继承 / 全局 / 主题 / 嵌套

2.1 动态样式:通过 Props 控制样式

styled-components 最核心的特性之一,无需拼接 className,直接通过 props 动态调整样式,适配状态切换、主题变化等场景。

import React from 'react';
import styled from 'styled-components';

// 带 props 的动态按钮
const DynamicButton = styled.button`
  padding: ${props => props.size === 'large' ? '12px 24px' : '8px 16px'};
  background-color: ${props => {
    switch (props.variant) {
      case 'primary': return '#2563eb';
      case 'danger': return '#dc3545';
      case 'success': return '#28a745';
      default: return '#6c757d';
    }
  }};
  color: white;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  &:hover { opacity: 0.9; }
`;

function DynamicStyleDemo() {
  return (
    <div style={{ gap: '10px', display: 'flex', padding: '20px' }}>
      <DynamicButton variant="primary" size="large">主要大按钮</DynamicButton>
      <DynamicButton variant="danger">危险默认按钮</DynamicButton>
      <DynamicButton variant="success">成功按钮</DynamicButton>
    </div>
  );
}

2.2 样式继承:复用已有样式

import styled from 'styled-components';

// 基础按钮(通用样式)
const BaseButton = styled.button`
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  color: white;
  cursor: pointer;
  font-size: 14px;
`;

// 继承基础按钮,扩展危险按钮样式
const DangerButton = styled(BaseButton)`
  background-color: #dc3545;
  &:hover { background-color: #c82333; }
`;

// 继承并覆盖样式:轮廓按钮
const OutlineButton = styled(BaseButton)`
  background-color: transparent;
  border: 1px solid #2563eb;
  color: #2563eb;
  &:hover {
    background-color: #2563eb;
    color: white;
    transition: all 0.2s ease;
  }
`;

2.3 全局样式:重置与全局配置

import React from 'react';
import styled, { createGlobalStyle } from 'styled-components';

// 全局样式组件
const GlobalStyle = createGlobalStyle`
  /* 重置浏览器默认样式 */
  * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }

  /* 全局字体和背景 */
  body {
    font-family: 'Microsoft YaHei', sans-serif;
    background-color: #f8f9fa;
    color: #333;
  }

  /* 全局链接样式 */
  a {
    text-decoration: none;
    color: #2563eb;
  }
`;

// 根组件中使用
function App() {
  return (
    <>
      <GlobalStyle /> {/* 全局样式生效 */}
      <div>应用内容...</div>
    </>
  );
}

2.4 主题管理(ThemeProvider):全局样式统一

import React, { useState } from 'react';
import styled, { ThemeProvider } from 'styled-components';

// 定义主题对象
const lightTheme = {
  colors: { primary: '#2563eb', background: '#f8f9fa', text: '#333' },
  fontSize: { small: '12px', medium: '14px' }
};
const darkTheme = {
  colors: { primary: '#198754', background: '#212529', text: '#fff' },
  fontSize: { small: '12px', medium: '14px' }
};

// 使用主题样式
const ThemedCard = styled.div`
  padding: 20px;
  background-color: ${props => props.theme.colors.background};
  color: ${props => props.theme.colors.text};
  border-radius: 8px;
`;
const ThemedButton = styled.button`
  padding: 8px 16px;
  background-color: ${props => props.theme.colors.primary};
  color: white;
  border: none;
  border-radius: 4px;
`;

function ThemeDemo() {
  const [isDark, setIsDark] = useState(false);
  return (
    <ThemeProvider theme={isDark ? darkTheme : lightTheme}>
      <div style={{ padding: '20px' }}>
        <button onClick={() => setIsDark(!isDark)}>
          切换{isDark ? '浅色' : '暗黑'}主题
        </button>
        <ThemedCard style={{ marginTop: '10px' }}>
          <ThemedButton>主题化按钮</ThemedButton>
        </ThemedCard>
      </div>
    </ThemeProvider>
  );
}

2.5 嵌套样式:模拟 SCSS 语法

const Card = styled.div`
  width: 300px;
  padding: 20px;
  border: 1px solid #eee;
  border-radius: 8px;

  /* 嵌套子元素样式 */
  .card-title {
    font-size: 20px;
    margin-bottom: 10px;
  }
  .card-content {
    font-size: 14px;
    /* 深层嵌套 */
    .highlight { color: #2563eb; }
  }
`;

3实战场景与最佳实践

3.1 什么时候用 styled-components?

3.2 注意事项与最佳实践

要点做法
避免过度嵌套嵌套层级建议不超过 2-3 层,否则可读性下降
自定义组件必传 className用 styled(组件) 时,确保组件将 className 传给根元素
慎用 !important覆盖第三方组件样式时,优先提高选择器优先级,而非直接用 !important
样式组件定义在外部避免在渲染函数内定义样式组件(导致每次渲染重新创建)
调试优化安装 babel-plugin-styled-components,让开发者工具显示有意义的 className
抽离通用样式将重复样式抽离为基础组件或主题变量,减少冗余
⚠️ 常见坑:在渲染函数内部定义 styled 组件会导致每次渲染重新生成类名、触发重复挂载与重排,务必把样式组件提升到模块顶层。

4术语表 & 附录:完整代码

术语表

术语定义
CSS-in-JS把 CSS 样式写在 JavaScript 中,与组件一一绑定的方案
styled.div / styled(组件)创建带样式的组件:原生标签快捷写法 / 通用函数写法
模板字符串插值样式模板中 ${props => ...} 动态计算样式值
createGlobalStyle创建全局样式组件,在根组件渲染一次即全局生效
ThemeProvider主题上下文提供者,通过 props.theme 向所有样式组件下发主题
& 选择器指向当前组件自身的引用,配合 :hover 等伪类使用
className 透传styled(组件) 要求组件把 className 传给根元素,样式才生效

附录 A:最小可运行 Demo(含 props 动态样式)

// App.jsx —— 复制即可运行
import React, { useState } from 'react';
import styled, { ThemeProvider } from 'styled-components';

const theme = {
  colors: { primary: '#2563eb', background: '#f8f9fa', text: '#333' }
};

const Card = styled.div`
  width: 320px;
  padding: 24px;
  background: ${p => p.theme.colors.background};
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.08);
  text-align: center;
`;

const Btn = styled.button`
  padding: ${p => (p.big ? '12px 28px' : '8px 16px')};
  background: ${p => p.theme.colors.primary};
  color: #fff;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  &:hover { opacity: 0.85; }
`;

export default function App() {
  const [count, setCount] = useState(0);
  return (
    <ThemeProvider theme={theme}>
      <Card>
        <h3>styled-components 最小 Demo</h3>
        <p>当前计数:{count}</p>
        <Btn big onClick={() => setCount(count + 1)}>+1</Btn>
      </Card>
    </ThemeProvider>
  );
}
✅ 总结:styled-components 并非简单的"CSS 写在 JS 里",而是 React 组件化思想在样式领域的延伸——语法灵活(全场景覆盖)、样式闭环(杜绝污染)、动态能力(props + 主题)、生态兼容(KendoReact/AntD),是中大型 React 项目样式管理的首选方案。