文章目录
-
- 1、普通方式-内联使用css
- 2、引入css文件
- 3、内联css与引入css文件对比
-
- 3.1、内联css
- [3.2、 外部 CSS 文件(External CSS)](#3.2、 外部 CSS 文件(External CSS))
- [4、css module](#4、css module)
- 5、sass
- [6、classnames组合scss modules](#6、classnames组合scss modules)
- 7、css-in-js
-
- [7.1、CSS-in-JS 的核心特性](#7.1、CSS-in-JS 的核心特性)
- [7.2、主流 CSS-in-JS 库对比](#7.2、主流 CSS-in-JS 库对比)
- [7.3、在 React 中的使用示例](#7.3、在 React 中的使用示例)
-
- [7.3.1、使用 styled-components](#7.3.1、使用 styled-components)
- [7.3.2、 使用 Emotion(推荐)](#7.3.2、 使用 Emotion(推荐))
- [7.4、CSS-in-JS 的优缺点](#7.4、CSS-in-JS 的优缺点)
- 7.5、性能优化策略
- [7.6、与传统 CSS 方案对比](#7.6、与传统 CSS 方案对比)
- 7.7、如何选择?
- 7.8、最佳实践
- 结语
1、普通方式-内联使用css
使用规则:
- 和HTML元素的style类似
- 必须是JS对象的写法,不能是字符串
- 样式名要驼峰式写法,如fontSize
优缺点:
- 优点:优先级最高,适合临时调试或覆盖特定样式。
- 缺点:难以维护,样式与内容混杂,无法复用。
- 适用场景:快速测试、临时修复或需要强制覆盖其他样式的场景。
2、引入css文件
2.1、示例
- 使用css文件
- JSX中使用className
- 可使用clsx或者classnames做条件判断
给已发布的div添加额外的样式,QuestionCard.css如下
css
.list-item {
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 16px;
}
.published {
border-color: green;
}
QuestionCard.tsx如下所示:
react
import { FC, useEffect } from "react";
import "./QuestionCard.css";
...
const QuestionCard: FC<PropsType> = (props) => {
...
// 已发布的添加样式
let itemClassName = "list-item";
isPublished && (itemClassName = itemClassName + " published");
return (
<div key={id} className={itemClassName}>
...
</div>
);
};
export default QuestionCard;
这种情况,逻辑稍微复杂的话,代码繁琐,不方便维护,这里我们用第三方库classnames来解决,clsx类似,不在演示。
2.2、classnames
详细安装使用可以参考下面链接1github中介绍,这里直接改造上面样式,QuestionCard.tsx代码如下:
react
import classnames from "classnames";
import "./QuestionCard.css";
const QuestionCard: FC<PropsType> = (props) => {
...
let itemClassName = classnames("list-item", { published: isPublished });
...
3、内联css与引入css文件对比
3.1、内联css
优点:
-
组件化作用域:样式天然限定在当前组件,无需担心全局污染。
-
动态样式灵活 :可直接基于
props
或state
动态计算样式:jsx
复制
<div style={{ opacity: isActive ? 1 : 0.5 }}>
-
无类名冲突:无需处理类名命名问题。
-
适合快速原型开发:简单场景下无需维护额外 CSS 文件。
缺点:
- 可维护性差:样式与逻辑混合,代码臃肿,难以复用。
- 功能限制 :无法直接使用伪类(
:hover
)、媒体查询、动画等 CSS 高级特性。 - 性能问题:频繁更新的动态样式可能触发不必要的重渲染。
- 优先级混乱:内联样式优先级较高,可能意外覆盖外部样式。
适用场景:
- 简单的动态样式(如条件显隐、动态尺寸)。
- 小型组件或临时调试。
- 需要快速验证样式效果的场景。
3.2、 外部 CSS 文件(External CSS)
优点:
- 样式与逻辑分离:代码更清晰,符合关注点分离原则。
- 完整 CSS 功能:支持伪类、媒体查询、动画等所有 CSS 特性。
- 复用性强:可跨组件复用样式(如全局主题、工具类)。
- 性能优化:浏览器可缓存 CSS 文件,减少重复加载。
- 工具链支持:与预处理器(Sass/Less)、PostCSS 无缝集成。
缺点:
- 全局污染风险
4、css module
基本使用,css文件默认命名格式 xxx.module.css,QuestionCard.module.css如下
css
.list-item {
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 16px;
}
.published {
border-color: green;
}
QuestionCard.tsx如下:
react
...
import styles from "./QuestionCard.module.css";
const QuestionCard: FC<PropsType> = (props) => {
...
return (
<div key={id} className={styles["list-item"]}>
...
</div>
);
};
export default QuestionCard;
效果如下图所示:
5、sass
-
css语法比较原始,如不能嵌套。
-
现在开发一般使用less、sass等预处理语言
-
Vite等工具原生支持Sass Module,后
第一步:安装sass
bash
yarn add sass -D
第二步:编辑样式文件,命名格式 xxxx.module.scss
scss
.list-item {
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 16px;
.published-span {
color: green;
}
}
.published {
border-color: green;
}
第三步:引入tsx
react
...
import styles from "./QuestionCard.module.scss";
const QuestionCard: FC<PropsType> = (props) => {
...
return (
<div key={id} className={styles["list-item"]}>
<strong>{title}</strong>
{/* 条件判断 */}
{isPublished ? (
<span className={styles["published-span"]}>已发布</span>
) : (
<span>未发布 </span>
)}
...
</div>
);
};
export default QuestionCard;
效果如下图所示;
6、classnames组合scss modules
以上面为例,给已发布的div设置绿色边框,QuestionCard.tsx如下所示:
react
...
import styles from "./QuestionCard.module.scss";
const QuestionCard: FC<PropsType> = (props) => {
...
// 已发布的添加样式
const listItemClass = styles["list-item"];
const publishedClass = styles["published"];
const itemClassName = classnames({
[listItemClass]: true,
[publishedClass]: isPublished,
});
return (
<div key={id} className={itemClassName}>
<strong>{title}</strong>
{/* 条件判断 */}
{isPublished ? (
<span className={styles["published-span"]}>已发布</span>
) : (
<span>未发布 </span>
)}
...
);
};
export default QuestionCard;
效果如下所示:

7、css-in-js
在 React 和现代前端开发中,CSS-in-JS 是一种将 CSS 样式直接编写在 JavaScript/JSX 中的技术方案,它通过组件化的方式管理样式,解决了传统 CSS 的全局作用域污染、类名冲突等问题。以下是 CSS-in-JS 的核心解析和主流方案对比:
7.1、CSS-in-JS 的核心特性
特性 | 说明 |
---|---|
组件化作用域 | 样式与组件绑定,天然隔离,避免全局污染 |
动态样式 | 基于 props 或 state 动态生成样式,无需手动切换类名 |
自动厂商前缀 | 自动为 CSS 属性添加浏览器前缀(如 -webkit- ) |
主题支持 | 通过 Provider 模式轻松实现主题切换 |
SSR 支持 | 支持服务端渲染(Server-Side Rendering) |
原子化 CSS | 部分库(如 Emotion)支持生成原子化 CSS 类,优化性能 |
7.2、主流 CSS-in-JS 库对比
库名 | 语法风格 | 优势 | 缺点 | 适用场景 |
---|---|---|---|---|
styled-components | 模板字符串 | 生态成熟,社区活跃 | 运行时开销较大 | 通用场景 |
Emotion | 模板字符串/Object | 高性能,支持原子化 CSS | 配置较复杂 | 性能敏感型项目 |
JSS | Object 样式 | 轻量级,框架无关 | 可读性较差 | 需要高度定制化的场景 |
Linaria | 模板字符串 | 零运行时,编译时生成 CSS | 动态样式受限 | 追求极致性能的静态站点 |
7.3、在 React 中的使用示例
7.3.1、使用 styled-components
bash
bash
yarn add styled-components
jsx
react
import { FC } from "react";
import styled from "styled-components";
// 基础组件
const Button = styled.button`
padding: 12px 24px;
background: ${(props) => (props.primary ? "#1890ff" : "#f5f5f5")};
color: ${(props) => (props.primary ? "white" : "#333")};
border-radius: 4px;
&:hover {
opacity: 0.8;
}
`;
// 继承样式
const LargeButton = styled(Button)`
padding: 16px 32px;
font-size: 18px;
`;
const Demo: FC = () => {
return (
<div>
<Button>默认按钮</Button>
<Button primary>主按钮</Button>
<LargeButton>大按钮</LargeButton>
</div>
);
};
export default Demo;
7.3.2、 使用 Emotion(推荐)
bash
yarn add @emotion/react @emotion/styled
jsx
react
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
import styled from '@emotion/styled';
// 1. 使用 css prop
function DynamicComponent({ isActive }) {
return (
<div
css={css`
padding: 20px;
background: ${isActive ? '#1890ff' : '#f5f5f5'};
&:hover {
opacity: 0.8;
}
`}
>
动态样式
</div>
);
}
// 2. 使用 styled API
const StyledButton = styled.button`
padding: 12px 24px;
border-radius: 4px;
${props => props.variant === 'primary' && css`
background: #1890ff;
color: white;
`}
`;
function App() {
return (
<StyledButton variant="primary">Emotion 按钮</StyledButton>
);
}
7.4、CSS-in-JS 的优缺点
7.4.1、优点
- 样式与组件共存:无需在多个文件间跳转,提升开发体验。
- 动态样式简便:直接基于组件状态或 props 驱动样式变化。
- 自动作用域隔离:避免类名冲突,适合大型项目。
- 主题和设计系统:通过 Context API 实现跨组件主题传递。
7.4.2、缺点
- 运行时开销:动态生成 CSS 可能影响性能(可通过原子化 CSS 优化)。
- 学习成本:需要熟悉新的 API 和模式。
- 调试困难:生成的类名哈希化,开发者工具中可读性差。
- 包体积增加:引入额外的运行时库(约 10-20 KB)。
7.5、性能优化策略
-
原子化 CSS
使用
@emotion/css
生成原子类,复用样式声明:jsx
复制
import { css } from '@emotion/css'; const flexCenter = css` display: flex; justify-content: center; align-items: center; `; function Component() { return <div className={flexCenter}>居中内容</div>; }
-
编译时提取 CSS
使用 Linaria 或 Compiled 在构建时生成静态 CSS 文件:
jsx
复制
// 使用 Linaria import { css } from 'linaria'; const title = css` font-size: 24px; color: #333; `; function Component() { return <h1 className={title}>标题</h1>; }
-
避免频繁样式更新
对于高频更新的样式(如动画),优先使用 CSS 原生动画或
transform
属性。
7.6、与传统 CSS 方案对比
场景 | CSS-in-JS | CSS Modules | 普通 CSS |
---|---|---|---|
作用域管理 | ✅ 自动隔离 | ✅ 哈希类名隔离 | ❌ 需手动管理 |
动态样式 | ✅ 基于 props/state | ❌ 需类名切换 | ❌ 需类名切换 |
伪类/媒体查询 | ✅ 完整支持 | ✅ 完整支持 | ✅ 完整支持 |
SSR 支持 | ✅ 完善 | ✅ 支持 | ✅ 支持 |
性能 | ⚠️ 运行时开销 | ✅ 高效 | ✅ 高效 |
适用项目规模 | 中大型动态项目 | 中大型项目 | 小型项目或遗留系统 |
7.7、如何选择?
- 选择 CSS-in-JS 的场景 :
- 项目需要高度动态的样式(如主题切换、复杂交互状态)。
- 团队偏好组件化开发模式,希望样式与逻辑紧密耦合。
- 项目规模较大,需严格避免样式冲突。
- 避免 CSS-in-JS 的场景 :
- 追求极致性能(如低端设备或动画密集型应用)。
- 需与非 React 生态共享样式(如跨框架组件库)。
- 项目已稳定使用 CSS Modules 或 Tailwind CSS。
7.8、最佳实践
-
统一样式模式:团队约定使用单一方案(如 Emotion),避免混用多种 CSS 方案。
-
主题规范化:使用 ThemeProvider 管理颜色、间距等设计系统变量:
jsx
复制
// 定义主题 const theme = { colors: { primary: '#1890ff' }, spacing: { md: '16px' } }; // 在根组件传递主题 import { ThemeProvider } from '@emotion/react'; function App() { return ( <ThemeProvider theme={theme}> <ChildComponent /> </ThemeProvider> ); } // 子组件中使用主题 const Button = styled.button` padding: ${props => props.theme.spacing.md}; background: ${props => props.theme.colors.primary}; `;
-
结合静态样式提取:对首屏关键 CSS 使用编译时工具生成,减少运行时开销。
CSS-in-JS 是现代 React 生态中强大的样式方案,尤其适合动态化、组件化的项目需求。根据团队习惯和性能要求选择合适的库,并遵循最佳实践,可显著提升开发效率和可维护性。
结语
❓QQ:806797785
⭐️仓库地址:https://gitee.com/gaogzhen
⭐️仓库地址:https://github.com/gaogzhen
1\][github classnames](https://github.com/JedWatson/classnames)\[CP/OL\]. \[2\][styled-component官网](https://styled-components.com/)\[CP/OL\]. \[3\][Classnames](https://jedwatson.github.io/classnames/)\[CP/OL\].