BEM和SMACSS是CSS组织约定而非语法,核心解决命名、拆分与复用问题;BEM强制三段式类名与模块化文件结构,SMACSS按职责分层类名与类型切片文件组织。

BEM 和 SMACSS 都不是 CSS 语法,而是组织类名和样式文件的约定;它们解决的不是“怎么写样式”,而是“怎么命名、拆分、复用样式”——选错或混用,后期改起来比重写还疼。
类名结构:BEM 强制层级嵌套,SMACSS 倾向语义分层
BEM 的类名必须体现 block、element、modifier 三段式关系,比如 menu__item--disabled。其中 menu 是独立模块,__item 只能属于它,--disabled 只能修饰 menu__item。这种写法杜绝了意外继承和跨模块污染,但也会让类名变长、重复感强。
SMACSS 把类名按职责归类:基础(btn)、布局(l-header)、模块(promo-card)、状态(is-hidden)、主题(theme-dark)。它不禁止组合,比如 promo-card is-hidden 是合法的;也不强制元素归属,promo-card-title 和 promo-card__title 都可能出现,取决于团队习惯。
- BEM 类名可预测性强,工具(如 VS Code 插件)能自动补全
__和--;SMACSS 类名更自由,但需靠文档或命名规范约束 - BEM 的
element不允许嵌套(menu__item__icon是反模式),SMACSS 中promo-card-title-icon虽不推荐但无硬性限制 - 当组件需要多层嵌套结构时,BEM 容易出现“深链”(
form__field-group__label--required),SMACSS 更倾向扁平拆分(field-group-label+is-required)
文件组织:BEM 推崇模块即文件,SMACSS 按类型切片
BEM 项目里常见一个 button/ 目录,里面放 button.css、button.js、button.test.js,样式只管这个 block。修改按钮样式,你只打开这一个目录。SMACSS 则把所有基础样式(base.css)、所有布局(layout.css)、所有模块(modules/ 下按功能分)分开存放。
立即学习“前端免费学习笔记(深入)”;
- BEM 文件定位快,但模块复用困难——比如两个不同
cardblock 各自维护,细微差异就导致样式冗余 - SMACSS 的
base.css容易变成“全局样式垃圾场”,h1 { margin: 0 }和.btn { display: inline-block }混在一起,后期想抽离某类规则非常痛苦 - 两者都依赖构建工具做合并,但 BEM 更适合配合 PostCSS 插件(如
postcss-bem)做类名校验,SMACSS 更依赖人工 review 类名前缀是否符合类型约定
与现代前端框架共存时,BEM 更易收敛,SMACSS 更易失控
在 React 或 Vue 里用 BEM,通常直接映射到组件结构:Card 组件输出 card、card__header、card--fluid,逻辑清晰。而 SMACSS 的状态类(如 is-loading)常被动态拼接进 className,容易漏掉空格或重复添加:className={`btn ${loading ? 'is-loading' : ''}` 写成 className={`btn${loading ? ' is-loading' : ''}` 就会出 bug。
- BEM 的 modifier 必须显式声明(
card--hover),不会因 JS 拼错导致样式失效;SMACSS 的is-类名没有命名约束,isLoad、isLoading、loading-state可能同时存在 - 使用 CSS-in-JS(如 Emotion)时,BEM 的块级作用域天然契合
css标签模板,SMACSS 的跨类型类名会让样式分散在多个css块中,难以追踪来源 - 服务端渲染(SSR)场景下,BEM 类名因结构固定,利于提取 critical CSS;SMACSS 若未严格分离 layout 和 module,首屏样式可能包含大量未用到的布局类
真正难的不是选 BEM 还是 SMACSS,而是团队能否坚持一套类名生成逻辑,并在 PR 里卡住不符合约定的提交。很多项目开头用 BEM,半年后出现 header__logo-link--big 和 header-logo-link-big 并存,问题不在架构,在人没把约定当接口契约来守。


















