bem与smacss不是互斥选择,而是分层协作:bem规范类名结构(block__element--modifier),smacss划分样式职责(base/layout/module/state/theme);前者管“怎么命名”,后者管“谁负责哪类样式”。

选BEM还是SMACSS,不取决于“哪个更先进”,而取决于你团队当前最痛的点在哪:如果类名冲突频繁、组件复用时样式互相污染、新同学总搞不清某个 btn-active 到底属于哪个模块——BEM 能立刻收住混乱;如果项目已有几十个页面,base.css 越来越臃肿,状态类(如 is-hidden)在 JS 里拼错三次、每次都要查 DOM 才能定位问题——SMACSS 的分层意识更能帮你划清责任边界。
什么时候该用 BEM:类名归属模糊、组件独立性差
BEM 强制把类名和模块绑定,card__title 就只能出现在 card 块里,不会和 user-card__title 或全局 title 冲突。它天然适合组件化开发场景,尤其是 React/Vue 单文件组件中,CSS 类名和 JSX 结构一一对应。
- 常见错误现象:
header__nav__item(嵌套元素)、button--large(没归属 block)、is-loading(无上下文的状态类) - 使用场景:UI 组件库、中后台系统、需要高复用率的卡片/表单/列表等模块
- 性能影响:每个模块单独打包 CSS 时,
postcss-bem可自动校验类名合法性,但跨模块复用困难——article-card和product-card很难共享同一套基础样式
什么时候该用 SMACSS:多人协作、职责分离需求明确
SMACSS 不规定类名怎么写,只规定“哪类样式该归谁管”。base.css 管重置和基础元素,layout.css 管栅格和容器,modules/card.css 只放卡片相关样式。它解决的是“改一个按钮样式,为什么要把整个 common.css 都拉出来看”的问题。
- 常见错误现象:
button-is-loading(把 state 塞进 module 名)、h1 { margin: 0 }和.btn { display: inline-block }混在同一个base.css里 - 使用场景:大型 Web App、Layout 层由前端架构师统一维护、Module 层按业务线拆分、State 类需与 Redux/Vuex 状态严格对齐
- 兼容性影响:JS 动态拼接类名时容易漏空格或重复添加,比如
className={`btn ${loading ? 'is-loading' : ''}`}缺少防错逻辑,建议配合工具函数封装
BEM 和 SMACSS 能不能混用?可以,但得守住边界
它们不是互斥方案,而是不同维度的约束:BEM 管“类名长什么样”,SMACSS 管“这个类名该放在哪个文件、由谁维护”。实践中常见组合是:用 BEM 写类名(promo-card__title--large),按 SMACSS 分类组织文件(modules/promo-card.css 归入 Module 层,state/loading.css 单独抽离)。
- 容易踩的坑:在
state.css里写.promo-card__title--loading(违反 State 层应无结构语义的原则) - 正确做法:State 类必须扁平、可叠加,如
is-loading、is-disabled,且只作用于已存在的模块类上 - 构建工具提示:BEM 类名校验插件(如
stylelint-selector-bem-pattern)和 SMACSS 文件分类 lint 规则(如stylelint-scss中的at-import-no-partial-leading-underscore)可以共存,但需分别配置
真正难的不是选模式,而是让所有人持续遵守。BEM 的 __ 和 -- 容易手误打成 _ 或 -,SMACSS 的 state 类一旦被 JS 直接写死在模板里,就再难抽离。工具链能兜底,但第一道防线永远是 Code Review 时多看一眼类名是否带上下文、文件路径是否符合分层约定。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











