CSS 模块化的实现方式
BEM 命名规范、CSS Modules 和 CSS in JS 三种方案对比
问题
如何实现 CSS 模块化,避免样式冲突和命名污染?
解答
BEM 命名规范
BEM 由 Yandex 团队提出,通过块(Block)、元素(Element)、修饰符(Modifier)的命名约定来组织 CSS 代码。
/* 块:独立的组件或模块 */
.block {
}
/* 元素:块中的组成部分,不能脱离块使用 */
.block__element {
}
/* 修饰符:定义块或元素的外观和行为 */
.block--modifier {
}
BEM 通过严格的命名规范让代码结构清晰,但本质上是约定而非强制,无法完全避免命名冲突。
CSS Modules
CSS Modules 像导入 JS 模块一样导入 CSS,打包时自动将类名转换为 hash 值,彻底解决命名冲突。
定义样式文件:
.className {
color: green;
}
/* 全局样式 */
:global(.className) {
color: red;
}
/* 样式复用 */
.otherClassName {
composes: className;
color: yellow;
}
在 JS 中使用:
import styles from "./style.css";
element.innerHTML = '<div class="' + styles.className + '">';
配置 webpack:
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: {
loader: 'css-loader',
options: {
modules: true
}
}
}
]
}
};
打包后的类名:
._2DHwuiHWMnKTOYG45T0x34 {
color: red;
}
._10B-buq6_BEOTOl9urIjf8 {
background-color: blue;
}
CSS in JS
使用 JS 编写 CSS,所有样式代码放在组件内部。目前有 40+ 种实现方案,最流行的是 styled-components。
import React from "react";
import styled from "styled-components";
// 创建带样式的组件
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;
// 通过属性动态定义样式
const Button = styled.button`
background: ${props => props.primary ? "palevioletred" : "white"};
color: ${props => props.primary ? "white" : "palevioletred"};
`;
其他流行的 CSS in JS 库:emotion、radium、glamorous。
关键点
- BEM 通过命名约定实现模块化,但依赖开发者自觉遵守规范
- CSS Modules 自动生成 hash 类名,彻底避免命名冲突,需要构建工具支持
- CSS in JS 将样式写在组件内部,支持动态样式,完全不需要单独的 CSS 文件
- 三种方案各有优劣:BEM 简单但不强制,CSS Modules 需要配置,CSS in JS 运行时开销较大
目录