bem 是用 block__element--modifier 结构将归属、层级、状态编码进类名,冲突变为可查的结构错误;判断 block 或 element 关键看能否独立存在和复用。

直接说结论:BEM 不是“给类名加长一点防冲突”,而是用 block__element--modifier 这种固定结构把归属、层级、状态全部编码进类名本身——冲突不再随机发生,而是变成一眼可查的结构错误。
怎么判断一个类该叫 block 还是 element
关键看它能不能独立存在、有没有复用价值。
- ✅
search-form是 block:能用在首页、弹窗、侧边栏,语义完整、样式自洽 - ✅
search-form__input是 element:离开search-form就没意义,不能单独复用 - ❌
.wrapper、.section-2不是 block:只是临时布局占位符,没业务语义 - ❌
.card__button__icon是错的:BEM 不允许三级嵌套;如果button本身有独立逻辑,它就该是自己的 block(比如button),icon是它的button__icon
Modifier 为什么必须用 -- 而不是 - 或省略
这不是风格问题,是工具链识别 BEM 结构的硬性锚点。漏写或错写会导致 lint 失效、VS Code 插件无法高亮、构建时样式提取出错。
- ❌
.button__icon_error:单下划线,会被stylelint-selector-bem-pattern拦截 - ❌
.card-header--large:漏掉一个_,card-header被识别为 block,但实际应是card块下的header元素 - ❌
.card__body--compact--dark:修饰符不支持嵌套,应写成.card__body--compact.card__body--dark(平级组合) - ✅ 正确做法:在项目中配置
stylelint-selector-bem-pattern,规则设为{"styleType": "bem"},CI 中跑npx stylelint "**/*.{css,scss}"阻断违规提交
SCSS 嵌套和 JS 动态拼接类名最容易踩什么坑
SCSS 里看似方便的嵌套,一不留神就编译出后代选择器,破坏 BEM 封装性;JS 手拼字符串看着快,实则在 CI 环境下极易因空格、连字符、大小写漏写而挂掉。
- ❌
.card { &__content { &__icon { } } }:编译出.card__content .card__content__icon,违反 BEM 元素不可嵌套原则 - ✅ 正确 SCSS 写法只允许单层:
.card { &__title { } &--featured { } } - ❌
className={`button button--${variant} ${hasIcon ? 'button__icon' : ''}`}:缺空格、button__icon脱离 block、无防错,Linux 构建可能直接报错 - ✅ 推荐封装常量:
const BLOCK = 'search-form'; const cn = (e, m) => `${BLOCK}${e ? '__' + e : ''}${m ? '--' + m : ''}`,调用cn('input', 'disabled')得到search-form__input--disabled
最易被忽略的一点:Modifier 名必须表达意图或状态(如 button--primary、search-form--inline),而非纯视觉描述(button--red、card--small)。否则换主题时要全局搜替换,而不是改一处变量就能生效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











