React 条件 class 的几种写法对比

FreeGuideOnline 最新 2026-07-05

jsx function Component({ isActive, isDisabled }) { return ( <div className={card ${isActive ? 'active' : ''} ${isDisabled ? 'disabled' : ''}}> 内容 ); }


优点:
- 零依赖,原生语法
- 适合极简场景

缺点:
- 会产生多余空格,需要手动处理 `undefined` 或 `false` 带来的冗余空白(React 会自动忽略 `false` / `null` / `undefined` 值,但对空格处理不完美)
- 条件复杂时,可读性急剧下降,括号嵌套难以维护
- 类名顺序无规律,不易阅读

### 二、数组 + join 方法

将每个类名视为数组元素,过滤掉假值后拼接。

```jsx
function Component({ isActive, isDisabled }) {
  const classes = [
    'card',
    isActive && 'active',
    isDisabled && 'disabled'
  ].filter(Boolean).join(' ');

  return <div className={classes}>内容</div>;
}

优点:

  • 避免了多余空格
  • 可读性稍好,逻辑清晰
  • 可以用变量提取,方便复用

缺点:

  • 仍然需要手动过滤 false 等值
  • 冗长,每次都要写 filter(Boolean).join(' ')
  • 动态条件与静态类名混在一起,对比度不高

三、classnames 库

最流行的解决方案,npm 周下载量过千万,已成为事实标准。

安装:

npm install classnames

使用:

import classNames from 'classnames';

function Component({ isActive, isDisabled }) {
  return (
    <div className={classNames('card', {
      active: isActive,
      disabled: isDisabled
    })}>
      内容
    </div>
  );
}

也可以混合字符串和条件:

classNames('btn', isPrimary && 'btn-primary', size && `btn-${size}`)

优点:

  • 语法简洁,对象键值对直观映射状态与类名
  • 自动忽略 falsenullundefined 等值,不会产生多余空格
  • 支持多种参数形式(字符串、对象、数组)
  • 生态成熟,几乎所有 React 项目都在用

缺点:

  • 引入了一个额外依赖(但体积极小,约 800 字节)

四、clsx 库

clsx 是比 classnames 更轻量、性能更优的替代品,API 基本兼容。

安装:

npm install clsx

使用:

import clsx from 'clsx';

function Component({ isActive, isDisabled }) {
  return (
    <div className={clsx('card', {
      active: isActive,
      disabled: isDisabled
    })}>
      内容
    </div>
  );
}

优点:

  • classnames 更快(基准测试显示约 2-3 倍优势)
  • 体积更小(228B gzipped)
  • API 几乎相同,切换成本极低
  • 支持条件对象、数组和嵌套参数

缺点:

  • 社区流行度稍低于 classnames,但趋势上涨
  • 同样需要额外依赖

五、CSS Modules 的条件组合

如果项目使用 CSS Modules,可以结合上述工具处理条件样式。

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

function Component({ isActive, isDisabled }) {
  return (
    <div className={clsx(styles.card, {
      [styles.active]: isActive,
      [styles.disabled]: isDisabled
    })}>
      内容
    </div>
  );
}

优点:

  • 与模块化样式天然结合,避免全局污染
  • 动态类名依然清晰

缺点:

  • 写法略长,需用计算属性名
  • 如果类名较多,对象结构可能显得重复

六、自定义 Hook 封装(进阶)

当项目中有大量重复的条件组合,可以封装一个 useClassName 钩子。

function useClassName(base, conditions) {
  return clsx(base, conditions);
}

function Component(props) {
  const className = useClassName('card', {
    active: props.isActive,
    disabled: props.isDisabled
  });
  return <div className={className}>内容</div>;
}