为什么现代前端项目不再只用传统 CSS?一篇给新手的样式方案选择指南

宋丽_后端
2025-12-23 04:51
阅读 275

大家好,我是小林,一名在一线大厂做前端开发的工程师,同时也是掘金上写了几十篇入门教程的老作者。今天这篇教程的起因很真实——上周我带的一位实习生问我:“为什么我们公司的 React 项目里写样式要用 styled-components,而不是直接写 .css 文件?”这个问题让我想起自己刚毕业时也困惑过:明明 HTML + CSS 能搞定页面,为什么还要搞出这么多新花样?

于是,我决定写一篇真正面向零基础初学者的文章,带你搞清楚 CSS-in-JS传统 CSS 到底是什么、怎么选、怎么用。无论你是刚学完 HTML/CSS 的小白,还是正在学 React/Vue 的新手,这篇文章都会帮你建立清晰的“样式方案”认知框架。


一、先搞懂:什么是样式方案?

简单说,样式方案 = 写 CSS 的方式

你可能已经知道,网页由三部分组成:

  • HTML:结构(比如按钮、段落)
  • CSS:样式(比如颜色、大小、位置)
  • JavaScript:行为(比如点击弹窗、数据加载)

传统做法是:写一个 style.css 文件,用类名(class)把样式“挂”到 HTML 元素上。

<!-- index.html -->
<button class="primary-btn">点击我</button>
/* style.css */
.primary-btn {
  background: blue;
  color: white;
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
}

这没问题!但当项目变大(比如一个包含上百个页面的产品),你会发现:

  • 类名冲突(两个组件都叫 .btn 怎么办?)
  • 样式难以复用(这个蓝色按钮在 A 页面和 B 页面长得一样,但代码要复制两遍)
  • 删除无用样式困难(删了某个组件,但不知道哪些 CSS 可以删)

于是,社区开始探索更工程化的解决方案,CSS-in-JS 就是其中之一。


二、环境准备:搭建你的第一个“样式实验场”

为了对比两种方案,我们需要一个最小的前端开发环境。别担心,我会手把手教你。

步骤 1:安装 Node.js

https://nodejs.org 下载 LTS 版本(长期支持版),安装即可。

验证是否成功:

node -v  # 应该输出 v18.x 或更高
npm -v   # 应该输出 8.x 或更高

步骤 2:创建 React 项目(零配置)

我们用 Vite,它比 Create React App 更快、更轻量。

npm create vite@latest my-style-demo -- --template react
cd my-style-demo
npm install

步骤 3:启动开发服务器

npm run dev

打开浏览器访问 http://localhost:5173,看到 Vite 的欢迎页就说明成功了!

💡 小贴士:如果你还没学 React,没关系!我们只用最基础的语法,重点在样式部分。React 组件就是一个函数返回 JSX(类似 HTML 的语法)。


三、核心概念:传统 CSS vs CSS-in-JS 到底差在哪?

传统 CSS:全局作用域的“老派优雅”

传统 CSS 的特点是 全局作用域 —— 你在任何 .css 文件里写的 .btn,整个网站都能用,但也意味着容易冲突。

为了解决这个问题,社区发展出很多“约定”:

  • BEM 命名法.button__primary--large
  • CSS Modules:自动给类名加哈希后缀(如 .btn_abc123
  • Sass/Less:支持变量、嵌套等增强语法

示例:用 CSS Modules 写一个按钮

  1. 创建 Button.module.css
/* src/components/Button.module.css */
.btn {
  background: #007bff;
  color: white;
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.btn:hover {
  background: #0056b3;
}
  1. 在 React 组件中使用
// src/components/Button.jsx
import styles from './Button.module.css';

export default function Button({ children }) {
  return <button className={styles.btn}>{children}</button>;
}

优点:熟悉、工具链成熟、性能好
缺点:仍需手动管理命名、动态样式(如根据 props 改颜色)写起来麻烦


CSS-in-JS:把样式写进 JavaScript 里

顾名思义,CSS-in-JS 就是在 JS 文件里写 CSS。最流行的库是 styled-components(React)和 emotion

它的核心思想是:每个组件自带样式,天然隔离,不怕冲突

安装 styled-components

npm install styled-components

示例:用 styled-components 写同一个按钮

// src/components/StyledButton.jsx
import styled from 'styled-components';

const StyledButton = styled.button`
  background: #007bff;
  color: white;
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;

  &:hover {
    background: #0056b3;
  }
`;

export default function StyledButton({ children }) {
  return <StyledButton>{children}</StyledButton>;
}

看!样式直接写在 JS 文件里,而且:

  • 不需要想类名(styled.button 自动生成唯一类名)
  • 支持 JavaScript 逻辑(比如根据 props 动态改样式)

进阶:动态样式(传统 CSS 很难做到!)

const DynamicButton = styled.button`
  background: ${props => props.variant === 'danger' ? '#dc3545' : '#007bff'};
  color: white;
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
`;

// 使用
<DynamicButton variant="danger">删除</DynamicButton>

优点:组件级隔离、动态样式强大、无需切换文件
缺点:运行时有轻微性能开销、学习新语法、调试需熟悉开发者工具


四、实战对比:用两种方案实现同一个产品需求

假设我们要做一个简单的“任务卡片”组件,要求:

  • 卡片有边框、内边距、阴影
  • 任务标题加粗
  • 完成状态的任务显示绿色对勾图标
  • 鼠标悬停时卡片微微上移

方案 1:传统 CSS + CSS Modules

/* TaskCard.module.css */
.card {
  border: 1px solid #ddd;
  padding: 16px;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  transition: transform 0.2s;
  margin-bottom: 16px;
}

.card:hover {
  transform: translateY(-2px);
}

.title {
  font-weight: bold;
  margin-bottom: 8px;
}

.completed .title::after {
  content: " ✅";
  color: green;
}
// TaskCard.jsx
import styles from './TaskCard.module.css';

export default function TaskCard({ title, completed }) {
  return (
    <div className={`${styles.card} ${completed ? styles.completed : ''}`}>
      <div className={styles.title}>{title}</div>
    </div>
  );
}

方案 2:CSS-in-JS (styled-components)

// StyledTaskCard.jsx
import styled from 'styled-components';

const Card = styled.div`
  border: 1px solid #ddd;
  padding: 16px;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  transition: transform 0.2s;
  margin-bottom: 16px;

  &:hover {
    transform: translateY(-2px);
  }

  ${props => props.completed && `
    .title::after {
      content: " ✅";
      color: green;
    }
  `}
`;

const Title = styled.div`
  font-weight: bold;
  margin-bottom: 8px;
`;

export default function StyledTaskCard({ title, completed }) {
  return (
    <Card completed={completed}>
      <Title className="title">{title}</Title>
    </Card>
  );
}

📌 注意:这里用了 className="title" 是为了在伪元素中引用。其实更好的写法是把 Title 也作为 prop 传入,但为简化对比保留此写法。


五、一张表看懂如何选择

对比维度 传统 CSS (含 Modules) CSS-in-JS
学习成本 低(前端必学基础) 中(需学新库)
样式隔离 需靠命名约定或 Modules 天然隔离
动态样式 难(需拼接类名或内联 style) 极易(直接用 JS 表达式)
性能 编译时生成,运行时无开销 运行时注入样式,有轻微开销
调试体验 开发者工具直接看 CSS 需理解生成的类名规则
适用场景 静态页面、内容型网站 复杂交互的 Web 应用、组件库

我的建议(来自 GitHub 热门项目的观察):

  • 如果你在做 营销页、博客、文档站 → 用传统 CSS + Tailwind CSS(原子化 CSS)
  • 如果你在做 后台系统、SaaS 产品、复杂交互应用 → 用 CSS-in-JS(如 styled-components)
  • 如果你在写 开源组件库 → 优先考虑传统 CSS(用户可能不用 React)

🔍 GitHub 趋势佐证:查看 styled-components(38k+ stars)和 Tailwind CSS(65k+ stars)的流行度,你会发现不同场景有不同王者。


六、新手常见问题解答

Q1:CSS-in-JS 会不会让 JS 文件变得臃肿?

不会。现代打包工具(如 Vite、Webpack)会把样式提取出来,最终产物仍是 CSS 文件(通过 babel-plugin-styled-components 等插件优化)。

Q2:我该现在就学 CSS-in-JS 吗?

如果你的目标是找工作,先精通传统 CSS(包括 Flex/Grid、响应式、BEM)。CSS-in-JS 是加分项,不是必需项。但如果你在用 React 做个人项目,大胆尝试!

Q3:有没有不用额外库的方案?

有!React/Vue 本身支持 内联样式(inline style),但功能有限:

// 内联样式示例
<div style={{ backgroundColor: 'blue', padding: '16px' }}>Hello</div>

缺点:不支持伪类(:hover)、媒体查询、样式复用。

Q4:Tailwind CSS 算哪一类?

Tailwind 是 原子化 CSS,属于传统 CSS 的增强方案。它用工具类(如 bg-blue-500 p-4)代替手写 CSS,既保持了 CSS 的性能,又避免了命名烦恼。值得单独学习!


七、下一步学习建议 & 避坑指南

学习路径推荐

  1. 夯实基础:确保你熟练掌握 CSS 盒模型、Flexbox、Grid、响应式设计
  2. 尝试 CSS Modules:在现有项目中用它解决类名冲突
  3. 玩转 styled-components:用它重构一个旧组件,体验动态样式的便利
  4. 了解 Tailwind:用 play.tailwindcss.com 快速上手
  5. 阅读源码:去看 styled-components 的 GitHub 仓库,理解它如何工作

我踩过的坑(希望你避开)

  • ❌ 不要为了用新技术而用新技术。先问:这个项目真的需要 CSS-in-JS 吗?
  • ❌ 避免在 CSS-in-JS 中写复杂逻辑。样式应保持简洁,复杂逻辑放回 JS 函数
  • ✅ 善用主题(Theme)功能。styled-components 支持全局主题,避免硬编码颜色值
  • ✅ 在团队项目中统一方案。不要一半人用 CSS Modules,一半人用 styled-components

结语:技术没有银弹,只有合适的选择

我当初学的时候,也曾以为“新=好”。后来在参与多个产品开发后才明白:工程决策的本质是权衡

传统 CSS 像是一把锋利的瑞士军刀——简单、可靠、通用;
CSS-in-JS 则像是一台智能数控机床——精准、灵活、适合复杂场景。

作为前端开发者,你的价值不是“会用最新技术”,而是“知道什么时候该用什么技术”。

希望这篇文章能帮你迈出理性选择的第一步。如果你觉得有用,欢迎去我的 GitHub 主页(@lincode)点个 star,那里有更多适合新手的实战项目!

最后留个小作业:用今天学到的知识,改造你的个人博客导航栏,分别用传统 CSS 和 CSS-in-JS 实现,对比感受差异。实践出真知!

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝