CSS-in-JS 和传统 CSS 到底该怎么选?React 新手必看样式方案指南
大家好,我是小林,一个在大厂干了3年前端开发的工程师,平时也在B站做技术UP主(欢迎关注!)。最近收到不少粉丝私信问我:“我刚开始学 React,写样式到底该用普通的 CSS 还是 CSS-in-JS 啊?”这个问题其实也是很多前端面试题的高频考点。
我当初学 React 的时候也特别纠结——看到 GitHub 上有的项目用 styled-components,有的用 .module.css,还有的直接写 <style> 标签,简直一头雾水。今天这篇教程,就带零基础的你彻底搞懂 CSS-in-JS 和 传统 CSS 的区别、适用场景,以及如何选择。
一、先搞清楚:什么是“样式方案”?
在前端开发中,样式方案就是指“你怎么给网页元素加颜色、大小、位置这些视觉效果”。
- 传统 CSS:就是我们最熟悉的
.css文件,用<link>引入 HTML。 - CSS-in-JS:把 CSS 写在 JavaScript 代码里,通常是用第三方库(比如
styled-components)来实现。
💡 举个生活化的例子:
- 传统 CSS 像是“先做好菜谱(CSS文件),再按菜谱炒菜(HTML)”
- CSS-in-JS 像是“边炒菜边写菜谱(样式和逻辑写在一起)”
二、环境准备:5分钟搭建 React 开发环境
我们要用 Vite 创建一个最简 React 项目(比 Create React App 快得多!):
# 1. 打开终端(Mac用Terminal,Win用PowerShell或CMD)
npm create vite@latest my-css-project -- --template react
# 2. 进入项目文件夹
cd my-css-project
# 3. 安装依赖
npm install
# 4. 启动开发服务器
npm run dev
打开浏览器访问 http://localhost:5173,看到 React 的欢迎页面就说明成功了!
✅ 小贴士:如果你还没装 Node.js,请先去 nodejs.org 下载 LTS 版本。
三、核心概念对比:传统 CSS vs CSS-in-JS
1. 传统 CSS(经典三件套)
这是最基础的方式,适合初学者理解样式机制。
// App.jsx
import './App.css';
function App() {
return <div className="card">Hello World</div>;
}
/* App.css */
.card {
background: #f0f0f0;
padding: 16px;
border-radius: 8px;
}
优点:
- 简单直观,学习成本低
- 浏览器原生支持,性能好
- 可以复用样式(比如多个组件用同一个
.btn类)
缺点:
- 全局作用域 → 容易样式冲突(比如两个组件都写了
.title) - 无法动态传参(比如根据 props 改变颜色)
2. CSS Modules(传统 CSS 的升级版)
为了解决“全局污染”问题,React 社区推出了 CSS Modules。
// App.jsx
import styles from './App.module.css';
function App() {
return <div className={styles.card}>Hello World</div>;
}
/* App.module.css */
.card {
background: #f0f0f0;
padding: 16px;
border-radius: 8px;
}
注意文件名必须带 .module.css!
Vite/React 会自动把 .card 编译成类似 App_card__abc123 的唯一类名,避免冲突。
优点:
- 保留 CSS 语法,学习平滑
- 自动局部作用域,不怕命名冲突
- 支持组合(composes)等高级特性
缺点:
- 不能直接使用 JavaScript 变量(比如
props.color)
3. CSS-in-JS(把样式写进 JS 里)
最流行的方案是 styled-components,我们来安装它:
npm install styled-components
然后这样写:
// App.jsx
import styled from 'styled-components';
const Card = styled.div`
background: #f0f0f0;
padding: 16px;
border-radius: 8px;
`;
function App() {
return <Card>Hello World</Card>;
}
更酷的是,它支持动态传参:
const Button = styled.button`
background: ${props => props.primary ? 'blue' : 'gray'};
color: white;
padding: 8px 16px;
`;
// 使用
<Button primary>主要按钮</Button>
<Button>普通按钮</Button>
优点:
- 样式与组件强绑定,天然局部作用域
- 支持 JavaScript 表达式,动态样式超方便
- 无需管理类名,代码更简洁
缺点:
- 需要额外学习库的语法
- 运行时有轻微性能开销(但现代浏览器基本无感)
- 不利于非开发者(如设计师)直接修改样式
四、实战:用三种方式写一个“用户卡片”组件
我们来做一个简单的用户信息卡片,包含头像、姓名、状态(在线/离线)。
方式1:传统 CSS
// UserCard.jsx
import './UserCard.css';
export default function UserCard({ name, status }) {
return (
<div className="user-card">
<img src="/avatar.png" alt="avatar" className="avatar" />
<div className="info">
<h3>{name}</h3>
<span className={`status ${status === 'online' ? 'online' : 'offline'}`}>
{status}
</span>
</div>
</div>
);
}
/* UserCard.css */
.user-card {
display: flex;
align-items: center;
padding: 12px;
border: 1px solid #ddd;
border-radius: 8px;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 12px;
}
.status.online { color: green; }
.status.offline { color: red; }
方式2:CSS Modules
// UserCard.jsx
import styles from './UserCard.module.css';
export default function UserCard({ name, status }) {
return (
<div className={styles.userCard}>
<img src="/avatar.png" alt="avatar" className={styles.avatar} />
<div className={styles.info}>
<h3>{name}</h3>
<span className={`${styles.status} ${styles[status]}`}>
{status}
</span>
</div>
</div>
);
}
/* UserCard.module.css */
.userCard {
display: flex;
align-items: center;
padding: 12px;
border: 1px solid #ddd;
border-radius: 8px;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 12px;
}
.status.online { color: green; }
.status.offline { color: red; }
方式3:CSS-in-JS(styled-components)
// UserCard.jsx
import styled from 'styled-components';
const Card = styled.div`
display: flex;
align-items: center;
padding: 12px;
border: 1px solid #ddd;
border-radius: 8px;
`;
const Avatar = styled.img`
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 12px;
`;
const Status = styled.span`
color: ${props => props.status === 'online' ? 'green' : 'red'};
`;
export default function UserCard({ name, status }) {
return (
<Card>
<Avatar src="/avatar.png" alt="avatar" />
<div>
<h3>{name}</h3>
<Status status={status}>{status}</Status>
</div>
</Card>
);
}
🔍 对比一下:
- 传统 CSS:需要手动管理类名,容易冲突
- CSS Modules:类名自动隔离,但动态样式写法别扭
- CSS-in-JS:逻辑和样式高度融合,动态样式一目了然
五、新手常见问题解答
Q1:我该选哪种方案?
| 场景 | 推荐方案 |
|---|---|
| 学习阶段 / 小型项目 | 传统 CSS 或 CSS Modules |
| 中大型 React 项目 | CSS Modules(主流)或 CSS-in-JS |
| 需要大量动态主题/样式 | CSS-in-JS(如 styled-components) |
| 团队有设计师参与 | 传统 CSS(设计师更熟悉) |
Q2:CSS-in-JS 会影响性能吗?
现代方案(如 styled-components v6+)已经做了大量优化,在 99% 的业务场景中性能差异可以忽略。除非你渲染上万个动态组件,否则不用焦虑。
Q3:GitHub 上为什么有人用 Emotion,有人用 styled-components?
它们都是 CSS-in-JS 库,功能类似。styled-components 语法更贴近 CSS,适合新手;Emotion 更轻量,支持 SSR 更好。初学者选 styled-components 即可。
Q4:面试官问“CSS-in-JS 优缺点”,怎么答?
✅ 标准回答模板: “CSS-in-JS 的核心优势是样式作用域隔离和动态样式支持,特别适合组件化开发。缺点是增加了运行时依赖,且对非 JS 开发者不够友好。在我们团队,对于需要主题切换的后台系统,我们会选用 styled-components;而对于静态展示型页面,优先使用 CSS Modules 保证性能和可维护性。”
六、避坑指南 & 学习建议
⚠️ 新手常犯的错误
在 CSS-in-JS 里写太多逻辑
❌ 错误:background: ${props => props.theme === 'dark' ? (props.disabled ? '#333' : '#000') : ...}
✅ 正确:把复杂逻辑抽成函数,保持样式简洁滥用 !important
无论是哪种方案,都尽量避免!important,它会破坏样式优先级体系。不抽离通用样式
比如按钮、输入框等,应该做成共享组件,而不是每个地方重复写。
📚 下一步学习路径
先掌握 CSS Modules
这是目前 React 项目中最主流的方案,GitHub 上大多数开源项目都用它。再尝试 styled-components
做一个小项目(比如 TodoList),体验动态样式的便利。了解 Tailwind CSS(可选)
这是另一种流行方案(原子化 CSS),和 CSS-in-JS 是不同思路,值得了解。深入学习 CSS 本身
无论用什么方案,扎实的 CSS 基础才是根本!推荐 MDN 的 CSS 教程。
七、总结:没有银弹,只有合适
- 传统 CSS:适合初学者打基础,简单项目够用
- CSS Modules:React 项目默认推荐,平衡了隔离性和性能
- CSS-in-JS:适合复杂交互、动态主题,但需权衡学习成本
我在大厂的真实经验:80% 的内部项目用 CSS Modules,20% 高度定制化的产品用 styled-components。你不需要“站队”,而是根据项目需求灵活选择。
最后,别忘了去 GitHub 上看看真实项目是怎么写的!比如:
- Vercel 官网:用 CSS Modules
- React Router 官网:用 Emotion
动手实践才是最好的老师。现在就打开你的编辑器,试试这三种写法吧!
如果觉得这篇教程有帮助,欢迎在评论区留言,或者去 B站 搜索“小林前端”关注我,我会持续更新零基础友好的实战教程!

评论 0