人人都会AI编程

15.1 主流 Vue 组件库对比

更新时间:2026-07-10

在 React 的世界里,样式方案的选择直接影响开发体验和项目的可维护性。CSS-in-JS 虽然火热,但传统方案——原生 CSS / SCSS 和 CSS Modules——凭借其简洁、高性能和与 Web 标准高度兼容的特点,依然是大量项目的首选。本小节将深入讲解这两种方案在 React 中的实践方式。

15.1.1 原生 CSS / SCSS 的 React 集成

原生 CSS 指的是直接编写 .css 文件并在组件中引入,SCSS(Sassy CSS)则是在 CSS 基础上增加了变量、嵌套、混入(mixin)等能力的预处理器。React 本身不对样式施加任何限制,你可以像普通 Web 项目一样直接使用它们。

基本用法

1. 全局样式引入

在入口文件(如 main.jsx)直接引入全局样式,这些样式会应用到整个应用:

/* styles/global.css */
body {
  font-family: 'Inter', sans-serif;
  margin: 0;
  background-color: #f5f5f5;
}

.btn {
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
}
// main.jsx
import './styles/global.css';
import App from './App';

2. 组件级样式

你也可以为每个组件编写独立的 CSS 文件,然后在组件中导入。这种方式简单直接,但需要注意样式是全局的,容易产生命名冲突:

/* components/Header.css */
.header {
  display: flex;
  justify-content: space-between;
  background: #fff;
  padding: 16px;
}

.header-title {
  font-size: 20px;
  font-weight: 600;
}
// components/Header.jsx
import './Header.css';

function Header() {
  return (
    <header className="header">
      <h1 className="header-title">我的应用</h1>
    </header>
  );
}

优点:简单,无需额外配置,熟悉 CSS 的开发者可以直接上手。

缺点:样式全局污染,大型项目中命名容易冲突;样式与组件分离,维护时需要在 JSX 和 CSS 文件之间频繁切换。

SCSS/SASS 提升样式编写体验

引入 SCSS 可以获得变量、嵌套、混入等特性,让样式代码更具组织和复用性。在 Vite 项目中安装 sass 依赖即可直接使用:

npm install -D sass
// styles/variables.scss
$primary-color: #1677ff;
$border-radius: 6px;

// components/Button.scss
@use '../styles/variables.scss' as *;

.btn {
  padding: 8px 16px;
  border-radius: $border-radius;
  background: $primary-color;
  color: white;
  border: none;
  cursor: pointer;

  &:hover {
    opacity: 0.8;
  }

  &.disabled {
    background: #ccc;
    cursor: not-allowed;
  }
}
import './Button.scss';

function Button({ children, disabled }) {
  return (
    <button className={`btn ${disabled ? 'disabled' : ''}`}>
      {children}
    </button>
  );
}

SCSS 的优势

  • 变量:统一管理颜色、间距、字体等设计令牌。
  • 嵌套:减少编写重复选择器,结构更清晰。
  • 混入:复用一组样式声明,比如清除浮动、文本溢出省略等。
  • 函数和运算:动态计算尺寸,例如 width: calc(100% - 20px); 可以写成 width: $full-width - 20px

但需要注意,导入 SCSS 文件依然是全局生效的,并没有解决样式隔离的困扰。

15.1.2 CSS Modules:组件作用域的样式隔离

CSS Modules 是一种权衡方案:它保留了编写原生 CSS/SCSS 的方式,但通过构建工具(Vite、Webpack)自动将类名转换为唯一的哈希串,从根本上避免了样式冲突。

如何使用 CSS Modules

React 项目使用 Vite 创建时,默认支持 CSS Modules。只需将文件后缀改为 .module.css.module.scss,然后以模块方式导入:

/* components/Header.module.css */
.header {
  display: flex;
  align-items: center;
  padding: 16px;
  background: #fff;
}

.title {
  font-size: 20px;
  font-weight: 600;
}
import styles from './Header.module.css';

function Header() {
  return (
    <header className={styles.header}>
      <h1 className={styles.title}>我的应用</h1>
    </header>
  );
}

编译后,styles.header 会变成类似 Header_header_1a2b3 的类名,styles.title 也会变成 Header_title_4c5d6。这样即使其他组件也定义了 .title,它们也会被编译成不同的类名,互不干扰。

CSS Modules 的核心特性

1. 作用域隔离

每个 CSS Module 生成唯一的类名,实现组件级样式隔离,不必再担心命名污染。

2. 组合(composes)

可以利用 composes 从其他模块或当前模块复用样式:

/* base.module.css */
.base {
  padding: 8px 16px;
  border-radius: 4px;
}

/* Button.module.css */
.primary {
  composes: base from './base.module.css';
  background: #1677ff;
  color: white;
}

.secondary {
  composes: base from './base.module.css';
  background: #f0f0f0;
  color: #333;
}

这让样式复用更加优雅,且没有增加 HTML 结构的负担。

3. 与预处理器结合

.module.scss 同样支持 SCSS 的所有特性:

/* variables.module.scss */
$primary: #1677ff;

/* Button.module.scss */
@use '../styles/variables.module.scss' as *;

.btn {
  padding: 8px 16px;
  border: none;
  background: $primary;
  color: white;

  &:disabled {
    background: #ccc;
  }
}
import styles from './Button.module.scss';

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

样式组合技巧

在 JSX 中使用多个模块类名时,推荐使用模板字符串或工具函数(如 clsx)来处理条件类名:

import clsx from 'clsx';
import styles from './Button.module.css';

function Button({ variant = 'primary', disabled }) {
  return (
    <button
      className={clsx(
        styles.btn,
        styles[variant],  // 动态类名
        disabled && styles.disabled
      )}
    >
      Click me
    </button>
  );
}

CSS Modules 的优势与局限

优势

  • 零运行时开销:在构建阶段生成哈希类名,没有额外的 JS 运行时消耗,性能最佳。
  • 接近原生 CSS 的开发体验:你仍然可以使用 CSS 所有功能(选择器、媒体查询、伪类等),不需要学习新的 API。
  • 类型安全(配合 TypeScript):可以使用插件(如 vite-plugin-sass-dts)为 CSS Modules 生成类型声明,让 styles.xxx 拥有智能提示。
  • 易于调试:在开发环境下类名通常包含文件名和原始类名,方便在 DevTools 中定位。

局限

  • 样式依然是静态的,无法在运行时基于组件状态动态生成样式(除非使用内联样式)。
  • 动态类名的写法稍显繁琐,需要借助 clsx 或模板字符串。
  • 不能像 CSS-in-JS 那样轻松实现基于 props 的样式派生(需要预定义多个变体类名)。

15.1.3 最佳实践与选型建议

对于大多数 React 项目,CSS Modules + SCSS 是一个兼顾了开发体验、可维护性和极致性能的黄金组合。你可以采用以下约定:

  1. 设计令牌集中管理:将颜色、间距、字体等定义在 SCSS 变量文件中,全局使用。
  2. 组件局部样式用 CSS Modules:每个组件对应一个 .module.scss 文件,样式与组件同目录放置。
  3. 全局样式谨慎使用:仅用于重置(reset)、通用排版、工具类,且放在单独的 global.scss 中。
  4. 类名命名遵循 BEM 精神:虽然在 CSS Modules 中冲突已解决,但清晰的类名仍有助于维护。
// components/Button.module.scss
@use '../../styles/variables' as *;

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 8px 16px;
  border-radius: $radius-sm;
  font-size: $font-md;
  transition: all 0.2s;
  border: none;
  cursor: pointer;

  // 变体
  &.primary {
    background: $color-primary;
    color: white;
    &:hover { background: lighten($color-primary, 8%); }
  }
  &.secondary {
    background: $color-bg;
    color: $color-text;
    &:hover { background: darken($color-bg, 5%); }
  }
  &.danger {
    background: red;
    color: white;
  }

  // 尺寸
  &.small {
    padding: 4px 12px;
    font-size: $font-sm;
  }
  &.large {
    padding: 12px 24px;
    font-size: $font-lg;
  }

  &:disabled {
    opacity: 0.6;
    cursor: not-allowed;
  }
}
import clsx from 'clsx';
import styles from './Button.module.scss';

function Button({ variant = 'primary', size = 'md', disabled, children, onClick }) {
  return (
    <button
      className={clsx(
        styles.button,
        styles[variant],
        size !== 'md' && styles[size]
      )}
      disabled={disabled}
      onClick={onClick}
    >
      {children}
    </button>
  );
}

这种模式已经在无数中大型项目中得到验证,它简洁、强大,且不会给运行时带来任何性能负担。如果你的团队对 CSS 较为熟悉,且追求极致的加载速度,CSS Modules 是毫无疑问的首选方案。