样式模块化编写

CSS 模块化的常见方案和实现思路

问题

抽离样式模块怎么写,说出思路。

解答

样式模块化的目标是解决 CSS 全局污染、命名冲突、复用困难等问题。常见方案如下:

1. BEM 命名规范

通过命名约定实现模块化,无需工具支持。

/* Block: 独立组件 */
.card {}

/* Element: 组件内部元素,用 __ 连接 */
.card__title {}
.card__content {}
.card__footer {}

/* Modifier: 状态或变体,用 -- 连接 */
.card--highlighted {}
.card__title--large {}
<div class="card card--highlighted">
  <h2 class="card__title card__title--large">标题</h2>
  <div class="card__content">内容</div>
</div>

2. CSS Modules

构建时自动生成唯一类名,彻底解决命名冲突。

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

.primary {
  background: #1890ff;
  color: white;
}

.disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
// Button.jsx
import styles from './Button.module.css';

function Button({ primary, disabled, children }) {
  // 编译后类名类似: Button_button_x7d2s
  const className = [
    styles.button,
    primary && styles.primary,
    disabled && styles.disabled
  ].filter(Boolean).join(' ');

  return <button className={className}>{children}</button>;
}

3. CSS-in-JS (styled-components)

样式与组件绑定,支持动态样式。

import styled from 'styled-components';

// 基础按钮样式
const Button = styled.button`
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  
  /* 根据 props 动态设置样式 */
  background: ${props => props.primary ? '#1890ff' : '#fff'};
  color: ${props => props.primary ? '#fff' : '#333'};
  
  &:hover {
    opacity: 0.8;
  }
  
  /* 禁用状态 */
  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
`;

// 继承扩展
const LargeButton = styled(Button)`
  padding: 12px 24px;
  font-size: 16px;
`;

// 使用
<Button primary>主要按钮</Button>
<LargeButton>大按钮</LargeButton>

4. Sass/Less 模块化

利用预处理器的功能组织样式。

// _variables.scss - 变量
$primary-color: #1890ff;
$border-radius: 4px;

// _mixins.scss - 混入
@mixin flex-center {
  display: flex;
  justify-content: center;
  align-items: center;
}

@mixin button-base {
  padding: 8px 16px;
  border: none;
  border-radius: $border-radius;
  cursor: pointer;
}

// _button.scss - 组件样式
@use 'variables' as *;
@use 'mixins' as *;

.button {
  @include button-base;
  
  &--primary {
    background: $primary-color;
    color: white;
  }
  
  &--icon {
    @include flex-center;
    gap: 4px;
  }
}

// main.scss - 入口文件
@use 'variables';
@use 'mixins';
@use 'button';
@use 'card';

5. 原子化 CSS (Tailwind)

通过组合原子类构建样式。

// 直接使用原子类
<button className="y47b9 qpcfs gk0te s8n5u tckc3 shmje">
  按钮
</button>

// 抽离为组件复用
function Button({ variant = 'primary', children }) {
  const variants = {
    primary: 'gk0te s8n5u shmje',
    secondary: 'rbm2y ljoxq wxlt3',
  };
  
  return (
    <button className={`y47b9 qpcfs tckc3 ${variants[variant]}`}>
      {children}
    </button>
  );
}
/* 或使用 @apply 抽离 */
.btn {
  @apply px-4 py-2 rounded;
}

.btn-primary {
  @apply bg-blue-500 text-white hover:bg-blue-600;
}

关键点

  • BEM:纯命名约定,无依赖,适合传统项目
  • CSS Modules:构建时生成唯一类名,零运行时开销
  • CSS-in-JS:样式与组件强绑定,支持动态样式,有运行时成本
  • Sass/Less:通过变量、混入、模块化语法组织代码
  • 原子化 CSS:高复用、低冗余,但类名较长,需要学习成本