说说 React 中引入 CSS 的方式有哪几种?区别?
问题解析(面试官考察点)
面试官通过此问题主要考察:
- 对 React 样式解决方案的了解
- 能否根据项目需求选择合适的方式
- 对 CSS 作用域和模块化理解
- 对现代 CSS-in-JS 方案的掌握
核心概念(基础知识点)
理想的 CSS 解决方案
一个好的 React CSS 解决方案应该具备:
- 局部作用域:不会随意污染其他组件
- 动态样式:可以根据组件状态生成样式
- 完整 CSS 特性:支持伪类、动画、媒体查询
- 开发体验:编写简洁、有代码提示
四种主要方式
- 在组件内直接使用:内联样式
- 组件中引入 .css 文件:传统 CSS
- 组件中引入 .module.css 文件:CSS Modules
- CSS in JS:styled-components、emotion 等
详细解答(代码示例)
方式一:在组件内直接使用(内联样式)
import React, { Component } from "react";
// 定义样式对象
const div1 = {
width: "300px",
margin: "30px auto",
backgroundColor: "#44014C", // 驼峰命名
minHeight: "200px",
boxSizing: "border-box"
};
class Test extends Component {
render() {
return (
<div>
{/* 使用样式对象 */}
<div style={div1}>123</div>
{/* 直接定义样式 */}
<div style={{ backgroundColor: "red", padding: "20px" }}>
直接内联样式
</div>
</div>
);
}
}
export default Test;
优点:
- 样式局部有效,不会冲突
- 可以动态获取 state 状态
缺点:
- 需要使用驼峰命名
- 没有代码提示
- 大量样式导致代码混乱
- 不支持伪类/伪元素
方式二:组件中引入 .css 文件
/* App.css */
.title {
color: red;
font-size: 20px;
}
.desc {
color: green;
text-decoration: underline;
}
import React, { PureComponent } from 'react';
import './App.css'; // 引入 CSS 文件
export default class App extends PureComponent {
render() {
return (
<div className="app">
<h2 className="title">我是 App 的标题</h2>
<p className="desc">我是 App 中的一段文字描述</p>
</div>
);
}
}
优点:
- 符合日常编写习惯
- 支持所有 CSS 特性
缺点:
- 全局作用域,样式会互相影响
- 容易产生命名冲突
方式三:引入 .module.css 文件(CSS Modules)
/* App.module.css */
.title {
color: red;
font-size: 20px;
}
.desc {
color: green;
text-decoration: underline;
}
import React, { PureComponent } from 'react';
import styles from './App.module.css'; // 作为模块引入
export default class App extends PureComponent {
render() {
return (
<div className={styles.app}>
<h2 className={styles.title}>我是 App 的标题</h2>
<p className={styles.desc}>我是 App 中的一段文字描述</p>
</div>
);
}
}
webpack 配置:
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true // 启用 CSS Modules
}
}
]
}
优点:
- 局部作用域,不会污染其他组件
- 编译后生成唯一类名
缺点:
- 类名不能使用连接符(如 .xxx-xx)
- 必须使用 {styles.className} 形式
- 不方便动态修改样式
方式四:CSS in JS(styled-components)
// style.js
import styled from 'styled-components';
export const SelfLink = styled.div`
height: 50px;
border: 1px solid red;
color: yellow;
`;
export const SelfButton = styled.div`
width: 150px;
height: 150px;
color: ${props => props.color};
background-image: url(${props => props.src});
background-size: 150px 150px;
/* 支持伪类 */
&:hover {
opacity: 0.8;
}
/* 支持媒体查询 */
@media (max-width: 768px) {
width: 100px;
height: 100px;
}
`;
import React, { Component } from "react";
import { SelfLink, SelfButton } from "./style";
class Test extends Component {
render() {
return (
<div>
<SelfLink title="People's Republic of China">
app.js
</SelfLink>
<SelfButton
color="palevioletred"
style={{ color: "pink" }}
src={fist}
>
SelfButton
</SelfButton>
</div>
);
}
}
export default Test;
优点:
- 真正的局部作用域
- 支持动态样式(基于 props)
- 支持所有 CSS 特性
- 样式与组件紧密结合
缺点:
- 需要学习新的语法
- 增加运行时开销
- 包体积增大
深入理解(原理剖析)
CSS Modules 的工作原理
/* Button.module.css */
.button {
background: blue;
}
.primary {
color: white;
}
编译后:
.Button_button__3K-4z {
background: blue;
}
.Button_primary__2K-9x {
color: white;
}
// 导入的是一个对象
import styles from './Button.module.css';
console.log(styles);
// {
// button: 'Button_button__3K-4z',
// primary: 'Button_primary__2K-9x'
// }
styled-components 的工作原理
const Button = styled.button`
background: ${props => props.primary ? 'blue' : 'white'};
color: ${props => props.primary ? 'white' : 'black'};
`;
执行过程:
- 解析模板字符串,提取 CSS
- 生成唯一的 class 名
- 创建 style 标签插入到 head
- 返回带 class 的 React 组件
四种方式对比
| 特性 | 内联样式 | 普通 CSS | CSS Modules | CSS in JS |
|---|---|---|---|---|
| 作用域 | 局部 | 全局 | 局部 | 局部 |
| 动态样式 | 支持 | 不支持 | 需要配合 | 原生支持 |
| 伪类/媒体查询 | 不支持 | 支持 | 支持 | 支持 |
| 代码量 | 多 | 少 | 中等 | 中等 |
| 学习成本 | 低 | 低 | 低 | 中等 |
| 运行时开销 | 无 | 无 | 无 | 有 |
最佳实践
推荐方案选择
-
小型项目 / 原型开发
- 使用 CSS Modules
- 简单、无额外依赖
-
中大型项目
- 使用 styled-components 或 emotion
- 更好的开发体验和可维护性
-
需要与设计师协作
- 使用普通 CSS + BEM 命名规范
- 设计师可以直接修改 CSS 文件
styled-components 最佳实践
// 主题配置
const theme = {
colors: {
primary: '#007bff',
secondary: '#6c757d',
success: '#28a745'
},
breakpoints: {
mobile: '768px',
tablet: '1024px'
}
};
// 基础组件
const Button = styled.button`
padding: 10px 20px;
border: none;
border-radius: 4px;
background: ${props => props.theme.colors.primary};
color: white;
cursor: pointer;
&:hover {
opacity: 0.9;
}
@media (max-width: ${props => props.theme.breakpoints.mobile}) {
padding: 8px 16px;
}
`;
// 扩展样式
const PrimaryButton = styled(Button)`
background: ${props => props.theme.colors.primary};
`;
const DangerButton = styled(Button)`
background: red;
`;
// 使用
function App() {
return (
<ThemeProvider theme={theme}>
<PrimaryButton>主要按钮</PrimaryButton>
<DangerButton>危险按钮</DangerButton>
</ThemeProvider>
);
}
CSS Modules 最佳实践
/* Button.module.css */
/* 使用 camelCase 命名 */
.button {
padding: 10px 20px;
border: none;
border-radius: 4px;
}
.buttonPrimary {
composes: button; /* 复用样式 */
background: blue;
color: white;
}
.buttonLarge {
composes: button;
padding: 15px 30px;
font-size: 18px;
}
import styles from './Button.module.css';
function Button({ primary, large, children }) {
const className = [
primary ? styles.buttonPrimary : styles.button,
large && styles.buttonLarge
].filter(Boolean).join(' ');
return <button className={className}>{children}</button>;
}
避免样式冲突的策略
// 1. 使用 CSS Modules
import styles from './Component.module.css';
// 2. 使用 BEM 命名规范
// Block__Element--Modifier
// .user-card__title--large
// 3. 使用 CSS in JS
const Component = styled.div``;
// 4. 使用 Scoped CSS(Vue 风格)
// 配合 postcss 插件实现
动态样式处理
// styled-components 动态样式
const Button = styled.button`
background: ${props => props.variant === 'primary' ? 'blue' : 'gray'};
opacity: ${props => props.disabled ? 0.5 : 1};
`;
// CSS Modules + classnames 库
import classNames from 'classnames';
import styles from './Button.module.css';
function Button({ variant, disabled, children }) {
const className = classNames({
[styles.button]: true,
[styles.buttonPrimary]: variant === 'primary',
[styles.buttonDisabled]: disabled
});
return <button className={className}>{children}</button>;
}
面试要点
-
四种引入方式
- 内联样式:简单但功能有限
- 普通 CSS:全局作用域,易冲突
- CSS Modules:局部作用域,编译时处理
- CSS in JS:运行时处理,功能最强大
-
选择依据
- 项目规模
- 团队技术栈
- 性能要求
- 维护性考虑
-
CSS Modules 原理
- 编译时转换类名
- 生成唯一标识
- 通过对象映射访问
-
CSS in JS 优缺点
- 优点:动态样式、局部作用域、JS 逻辑
- 缺点:运行时开销、包体积、学习成本
-
常见面试题
- React 中引入 CSS 的方式有哪些?
- CSS Modules 的原理是什么?
- styled-components 的优缺点?
- 如何解决 CSS 全局污染问题?
-
现代趋势
- CSS in JS 越来越流行
- Tailwind CSS 原子类方案
- 服务端组件可能影响 CSS in JS