在 React 项目开发中,组件不仅需要实现业务逻辑,还需要具备良好的视觉表现。随着项目规模不断扩大,如何组织样式代码、避免样式冲突、实现主题切换以及提高样式复用性,逐渐成为前端工程化开发中不可忽视的问题。
React 本身并没有规定必须使用哪一种 CSS 方案。开发者既可以使用传统 CSS,也可以使用 CSS Modules、Sass、Less、CSS-in-JS 等方案,还可以结合 Tailwind CSS、Ant Design 等组件库完成页面开发。
那么,React 中究竟有哪些样式编写方式?它们分别有什么特点?在实际项目中应该如何使用?企业级后台管理系统又应该如何选择?
本文将从基础语法、常见方案、代码示例、工程化组织以及企业级项目选型等方面进行详细介绍。
React 中常见的样式方案主要包括以下几种:
需要注意的是,这些方案并不完全处于同一个层级。例如,Sass 和 Less 属于 CSS 预处理器,而 CSS Modules 主要解决样式作用域问题,CSS-in-JS 则侧重于在 JavaScript 环境中组织和生成样式。
实际项目可以根据需求组合使用多种方案,而不是只能选择其中一种。
普通 CSS 是浏览器原生支持的样式方案,也是 React 最容易上手的样式编写方式。
首先创建一个 React 组件。
文件:src/components/user-card/index.tsx
import './index.css';
function UserCard() {
return (
<div className="user-card">
<h3 className="user-card-title">用户信息</h3>
<p className="user-card-description">这里展示用户的基本信息。</p>
</div>
);
}
export default UserCard;
创建对应的 CSS 文件。
文件:src/components/user-card/index.css
.user-card {
padding: 24px;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 8px rgb(0 0 0 / 8%);
}
.user-card-title {
margin: 0 0 12px;
font-size: 18px;
font-weight: 600;
}
.user-card-description {
margin: 0;
color: #666666;
font-size: 14px;
}
在组件中通过 import 引入 CSS 文件,构建工具会负责处理样式资源。
普通 CSS 默认使用全局作用域。
例如,两个组件都定义了相同的类名:
.title {
color: red;
}
.title {
color: blue;
}
由于类名处于同一个全局 CSS 命名空间中,最终样式可能受到加载顺序、选择器优先级等因素影响。
在大型项目中,如果所有组件都使用普通 CSS,就容易出现以下问题:
因此,普通 CSS 并不是不能用于大型项目,而是需要配合合理的命名规范、目录结构和样式隔离策略。
React 支持通过元素的 style 属性设置行内样式。
与 HTML 中的字符串形式不同,React 的 style 接收的是一个 JavaScript 对象。
function UserCard() {
return (
<div
style={{
padding: 24,
backgroundColor: '#ffffff',
borderRadius: 8,
}}
>
<h3 style={{ fontSize: 18, color: '#1677ff' }}>
用户信息
</h3>
</div>
);
}
export default UserCard;
这里需要注意几个规则:
第一,CSS 属性名通常使用驼峰命名法,例如 background-color 对应 backgroundColor。
第二,大多数长度属性可以使用数字表示像素值,例如 padding: 24 相当于 padding: 24px。
第三,某些属性可以直接使用数字,例如 opacity: 0.5。
第四,CSS 自定义属性需要使用字符串形式的属性名。
Inline Styles 的一个特点是可以直接根据组件状态动态生成样式。
import { useState } from 'react';
function StatusButton() {
const [active, setActive] = useState(false);
return (
<button
style={{
color: active ? '#ffffff' : '#1677ff',
backgroundColor: active ? '#1677ff' : '#ffffff',
border: '1px solid #1677ff',
padding: '8px 16px',
borderRadius: 6,
}}
onClick={() => setActive(!active)}
>
{active ? '已激活' : '未激活'}
</button>
);
}
export default StatusButton;
Inline Styles 适合少量动态样式,但不适合承担整个项目的样式体系。
它存在以下局限:
:hover、:focus 等伪类。因此,建议将 Inline Styles 用于少量、简单、确实需要动态计算的样式,而不是把整个页面的 CSS 全部转换成 JavaScript 对象。
CSS Modules 是 React 项目中常见的样式隔离方案。
它的核心思想是:通过构建工具将 CSS 类名转换为具有局部作用域的类名,避免不同组件之间的类名冲突。
例如:
.title {
color: red;
}
经过 CSS Modules 处理后,实际生成的类名可能类似:
title_a8f3k
具体生成规则取决于构建工具配置。
在 Vite 项目中,通常可以直接使用 .module.css 文件。
文件:src/components/user-card/index.module.css
.card {
padding: 24px;
background-color: #ffffff;
border-radius: 8px;
}
.title {
margin-bottom: 12px;
color: #1677ff;
font-size: 18px;
}
组件代码:
文件:src/components/user-card/index.tsx
import styles from './index.module.css';
function UserCard() {
return (
<div className={styles.card}>
<h3 className={styles.title}>用户信息</h3>
</div>
);
}
export default UserCard;
这里的 styles 是 CSS Modules 生成的类名映射对象。
实际开发中,经常需要根据状态切换样式。
可以结合模板字符串实现:
import { useState } from 'react';
import styles from './index.module.css';
function StatusCard() {
const [active, setActive] = useState(false);
return (
<div
className={`${styles.card} ${active ? styles.active : ''}`}
onClick={() => setActive(!active)}
>
点击切换状态
</div>
);
}
export default StatusCard;
如果项目中有大量条件类名,也可以使用 clsx 等工具简化写法。
CSS Modules 主要解决的是样式作用域问题,并不会自动解决所有样式管理问题。
例如:
因此,CSS Modules 更适合希望继续使用传统 CSS,同时需要组件级样式隔离的项目。
Sass 是一种 CSS 预处理器,可以通过变量、嵌套、混入等功能提高样式代码的组织能力。
SCSS 是 Sass 的一种语法形式,它与普通 CSS 的语法更加接近。
$primary-color: #1677ff;
$border-radius: 8px;
.user-card {
padding: 24px;
border-radius: $border-radius;
background-color: #ffffff;
.title {
color: $primary-color;
font-size: 18px;
}
}
SCSS 支持嵌套编写选择器。
.user-card {
padding: 24px;
.title {
font-size: 18px;
}
.description {
color: #666666;
}
&:hover {
box-shadow: 0 4px 12px rgb(0 0 0 / 10%);
}
}
其中 & 表示当前父级选择器。
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.user-card {
@include flex-center;
min-height: 100px;
}
混入适合封装需要重复使用的样式片段。
在 Vite 项目中,安装 Sass:
pnpm add -D sass
然后创建 .scss 文件:
.user-card {
padding: 24px;
.title {
color: #1677ff;
}
}
在组件中导入:
import './index.scss';
也可以结合 CSS Modules 使用:
index.module.scss
import styles from './index.module.scss';
function UserCard() {
return <div className={styles.card}>用户信息</div>;
}
export default UserCard;
Sass 适合以下场景:
需要注意的是,Sass 变量主要在构建阶段处理。如果需要在浏览器运行过程中动态切换主题,通常还需要结合 CSS 自定义属性或其他主题管理方案。
Less 与 Sass 的定位比较接近,同样提供变量、嵌套、混入等功能。
@primary-color: #1677ff;
@border-radius: 8px;
.user-card {
padding: 24px;
border-radius: @border-radius;
.title {
color: @primary-color;
font-size: 18px;
}
&:hover {
background-color: #f5f5f5;
}
}
Less 使用 @ 定义变量,而 SCSS 使用 $。
在 Vite 项目中安装 Less:
pnpm add -D less
创建样式文件:
index.less
在组件中导入:
import './index.less';
如果需要局部作用域,也可以使用:
index.module.less
具体模块处理能力取决于项目使用的构建工具及其配置。
Less 和 Sass 都可以完成大多数传统 CSS 工程化需求。
选择时可以考虑:
$variable | @variable | |
如果项目已经使用其中一种预处理器,通常没有必要为了少量语法差异而迁移。
对于新项目,应优先考虑团队习惯、现有依赖、组件库要求以及长期维护成本。
CSS-in-JS 并不是某一个特定的库,而是一类样式开发方式。
它的核心思想是将样式定义放在 JavaScript 或 TypeScript 环境中,使样式能够更加直接地使用组件状态、主题配置和运行时数据。
常见的 CSS-in-JS 方案包括:
需要注意,不同 CSS-in-JS 库的实现方式并不完全相同。有些方案会在运行时生成样式,有些则支持不同程度的静态提取。
Emotion 提供了多种样式编写方式,下面以 @emotion/styled 为例。
安装依赖:
pnpm add @emotion/react @emotion/styled
创建样式组件:
import styled from '@emotion/styled';
const UserCard = styled.div`
padding: 24px;
background-color: #ffffff;
border-radius: 8px;
&:hover {
box-shadow: 0 4px 12px rgb(0 0 0 / 10%);
}
`;
function UserInfo() {
return (
<UserCard>
<h3>用户信息</h3>
<p>这里展示用户的基本信息。</p>
</UserCard>
);
}
export default UserInfo;
这里的 UserCard 不再是普通 HTML 标签,而是一个带有样式的 React 组件。
import styled from '@emotion/styled';
interface StatusProps {
active: boolean;
}
const StatusBox = styled.div<StatusProps>`
padding: 16px;
color: ${({ active }) => (active ? '#1677ff' : '#666666')};
background-color: ${({ active }) => (active ? '#e6f4ff' : '#f5f5f5')};
border-radius: 8px;
`;
function StatusCard() {
return (
<StatusBox active>
当前状态:已激活
</StatusBox>
);
}
export default StatusCard;
这种方式可以直接根据组件属性生成对应样式,适合状态较多的业务组件。
CSS-in-JS 并不意味着一定比传统 CSS 更好。
在选型时还需要考虑:
此外,不同 CSS-in-JS 库的维护状态、API 设计和运行机制可能存在差异,选型时应结合项目实际情况。
Styled Components 是 CSS-in-JS 生态中较有代表性的方案。
它通过模板字符串创建具有样式的组件。
安装:
pnpm add styled-components
pnpm add -D @types/styled-components
对于使用 TypeScript 的项目,应根据所安装版本的类型声明情况确认是否需要额外安装类型包。
组件代码:
import styled from 'styled-components';
const Button = styled.button`
padding: 8px 16px;
color: #ffffff;
background-color: #1677ff;
border: none;
border-radius: 6px;
cursor: pointer;
&:hover {
background-color: #4096ff;
}
`;
function App() {
return <Button>提交</Button>;
}
export default App;
import styled from 'styled-components';
interface ButtonProps {
$danger?: boolean;
}
const Button = styled.button<ButtonProps>`
padding: 8px 16px;
color: #ffffff;
background-color: ${({ $danger }) => ($danger ? '#ff4d4f' : '#1677ff')};
border: none;
border-radius: 6px;
`;
function App() {
return (
<>
<Button>普通按钮</Button>
<Button $danger>危险按钮</Button>
</>
);
}
export default App;
这里使用 $danger 作为样式属性,避免将仅用于样式计算的属性直接传递到原生 DOM 元素。
Styled Components 适合喜欢组件化样式定义的团队,但对于已经使用 Ant Design 的项目,需要额外考虑组件库主题体系与样式方案之间的配合。
Tailwind CSS 是一种原子化 CSS 框架。
它提供大量预定义的工具类,开发者可以直接在 JSX 中组合这些类来完成页面布局和样式设计。
function UserCard() {
return (
<div className="rounded-lg bg-white p-6 shadow">
<h3 className="mb-3 text-lg font-semibold text-blue-600">
用户信息
</h3>
<p className="text-sm text-gray-500">
这里展示用户的基本信息。
</p>
</div>
);
}
export default UserCard;
例如:
p-6 | |
rounded-lg | |
bg-white | |
shadow | |
text-lg | |
font-semibold |
interface StatusProps {
active: boolean;
}
function StatusCard({ active }: StatusProps) {
return (
<div
className={
active
? 'rounded-lg bg-blue-50 p-4 text-blue-600'
: 'rounded-lg bg-gray-50 p-4 text-gray-500'
}
>
{active ? '已激活' : '未激活'}
</div>
);
}
export default StatusCard;
优点:
不足:
Tailwind CSS 适合喜欢原子化样式开发的团队,但不是所有项目都必须采用这种方式。
对于基于 React 和 Ant Design 的企业级后台管理系统,可以考虑使用 antd-style 组织项目样式。
antd-style 提供了与 Ant Design 设计体系配合的样式能力,可以结合主题 Token、样式 Hook 等机制管理组件样式。
它适合需要统一主题、动态样式和组件级样式组织的项目。
在确认项目使用的 React、Ant Design 以及 antd-style 版本兼容后,可以安装对应依赖:
pnpm add antd-style
实际项目应以所选版本的官方文档为准,确认依赖要求及 API。
下面展示一种常见的 createStyles 使用模式:
import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token, css }) => ({
card: css`
padding: 24px;
background-color: ${token.colorBgContainer};
border-radius: ${token.borderRadiusLG}px;
border: 1px solid ${token.colorBorderSecondary};
`,
title: css`
margin: 0 0 12px;
color: ${token.colorText};
font-size: 18px;
font-weight: 600;
`,
description: css`
margin: 0;
color: ${token.colorTextSecondary};
font-size: 14px;
`,
}));
function UserCard() {
const { styles } = useStyles();
return (
<div className={styles.card}>
<h3 className={styles.title}>用户信息</h3>
<p className={styles.description}>
这里展示用户的基本信息。
</p>
</div>
);
}
export default UserCard;
在这个例子中:
createStyles 用于定义组件样式。token 用于读取主题 Token。css 用于编写 CSS 样式。useStyles 在组件中获取生成的样式类名。具体 API 可能随版本变化,使用时应参考对应版本的官方文档。
在企业级项目中,不建议在每个组件中随意定义颜色、圆角和间距。
例如,不推荐到处写:
color: '#1677ff';
更适合的方式是使用统一的设计 Token:
color: token.colorPrimary;
这样当项目切换主题或调整品牌色时,就可以通过统一的主题配置管理相关样式。
例如:
const useStyles = createStyles(({ token }) => ({
button: {
color: token.colorTextLightSolid,
backgroundColor: token.colorPrimary,
borderRadius: token.borderRadius,
},
}));
这里展示的是使用主题变量的思路,实际样式对象的类型和 API 应以安装版本为准。
假设一个后台管理系统有几十个页面,如果每个页面都单独设置主色:
color: '#1677ff';
当需要统一修改品牌色时,就可能需要修改大量文件。
而使用主题 Token 后,可以将颜色定义集中到主题系统中。
例如:
主题配置
|
├── 主色
├── 背景色
├── 文本颜色
├── 边框颜色
├── 圆角
└── 间距
|
v
页面与业务组件
这样可以降低样式维护成本,并为亮色主题、暗色主题以及紧凑布局等功能提供基础。
需要注意,antd-style 并不是唯一的 CSS-in-JS 方案。是否采用它,应结合 Ant Design 版本、项目样式架构和团队习惯进行评估。
无论选择哪一种样式方案,都建议在项目中明确区分全局样式、组件样式和主题样式。
全局样式主要负责整个应用共享的基础规则,例如:
如果使用 CSS 文件,可以建立:
src/
├── styles/
│ ├── reset.css
│ ├── global.css
│ └── variables.css
└── main.tsx
在应用入口统一导入:
import './styles/reset.css';
import './styles/global.css';
全局样式应保持精简,避免将业务组件样式全部堆积在全局文件中。
组件样式应该尽可能与组件的职责保持一致。
例如:
src/
├── components/
│ ├── user-card/
│ │ ├── index.tsx
│ │ └── index.module.css
│ └── status-tag/
│ ├── index.tsx
│ └── index.module.css
如果项目采用 CSS-in-JS,也可以将样式定义放在组件文件内部,或者单独拆分为样式文件。
主题样式负责管理全局设计变量,例如:
主题
├── colorPrimary
├── colorBgContainer
├── colorText
├── colorBorder
├── borderRadius
└── spacing
主题系统应与业务组件解耦。
业务组件需要颜色时,优先读取主题 Token,而不是在每个组件中重复定义颜色。
对于企业级后台管理系统,可以根据项目选择的样式方案采用不同的目录组织方式。
src/
├── styles/
│ ├── reset.css
│ ├── global.css
│ └── variables.css
├── components/
│ ├── user-card/
│ │ ├── index.tsx
│ │ └── index.module.css
│ └── status-tag/
│ ├── index.tsx
│ └── index.module.css
└── pages/
└── user/
├── index.tsx
└── index.module.css
这种组织方式将组件逻辑和样式文件放在一起,适合传统 CSS 工程化项目。
src/
├── styles/
│ ├── global.ts
│ └── theme.ts
├── components/
│ ├── user-card/
│ │ └── index.tsx
│ └── status-tag/
│ └── index.tsx
└── pages/
└── user/
└── index.tsx
如果样式逻辑比较复杂,也可以在组件目录下创建独立的 style.ts 文件。
例如:
user-card/
├── index.tsx
└── style.ts
这样既能保持样式与组件之间的关联,也能避免单个组件文件过于庞大。
无论使用哪一种方案,都建议遵循以下原则:
样式方案的选择不应该只看语法是否简洁,还需要考虑团队习惯、组件库、主题需求、项目规模以及维护成本。
下面从常见场景进行分析。
如果项目页面较少,组件结构简单,可以考虑:
如果希望减少样式冲突,可以优先考虑 CSS Modules。
如果项目具有大量业务组件,可以考虑:
选择时需要结合团队对传统 CSS 或原子化 CSS 的熟悉程度。
如果项目基于 Ant Design,并且具有统一主题、动态主题、组件级样式等需求,可以考虑:
antd-style。其中,antd-style 可以作为与 Ant Design 设计 Token 配合的候选方案;CSS Modules 则适合希望保持样式文件独立的团队。
表格中的“支持”并不意味着不需要配置。主题系统、服务端渲染、样式提取以及构建工具兼容性,仍然需要根据具体方案和版本进行确认。
不建议。
对于简单组件,可以直接在组件中定义样式;对于样式较多、布局复杂的组件,可以将样式独立拆分。
关键在于保持组件逻辑清晰,而不是强制所有样式必须放在同一个文件中。
没有必要。
普通 CSS 仍然适合全局基础样式、第三方组件覆盖以及一些简单布局。
即使项目采用 CSS-in-JS,也可能需要保留少量全局 CSS。
可以。
例如,项目可以使用 CSS Modules 编写页面布局,同时使用 CSS-in-JS 实现需要动态主题的业务组件。
但应明确各自的使用边界,避免团队成员随意混用,导致样式体系复杂化。
应谨慎使用。
!important 可以提高声明的优先级,但大量使用会增加样式覆盖和维护难度。
更好的方式通常是检查:
建议:
React 本身并不限制样式的编写方式。普通 CSS、Inline Styles、CSS Modules、Sass、Less、CSS-in-JS、Tailwind CSS 等方案,都可以用于 React 项目开发。
不同方案解决的问题并不完全相同:
对于企业级项目,真正重要的并不是追求某一种流行技术,而是建立一套统一、可维护、可扩展的样式规范。
如果项目基于 React + TypeScript + Ant Design,并且需要统一主题、亮色与暗色切换以及组件级动态样式,那么可以将 antd-style 作为候选方案;如果团队更习惯传统 CSS,则可以考虑 CSS Modules 配合 CSS 自定义属性实现样式隔离和主题管理。
无论最终选择哪种方案,都应该坚持全局样式统一管理、组件样式职责清晰、设计 Token 集中维护这几个基本原则。这样才能让项目在持续迭代过程中保持良好的可读性和可维护性。