bem通过block__element--modifier结构将归属、层级、状态锁进类名,使样式冲突从随机覆盖变为可查的结构错误;__和--是工具链识别硬性锚点,不可替换或省略。

直接用 .button 或 .header 必然导致样式冲突,BEM 不是靠“名字更长”防撞车,而是用 block__element--modifier 把归属、层级、状态全锁进类名里——冲突不再随机发生,而变成一眼可查的结构错误。
为什么 __ 和 -- 不能替换成 - 或省略
这不是风格偏好,是工具链识别 BEM 结构的硬性锚点。所有 lint 工具、VS Code 插件、Webpack CSS 提取逻辑都依赖 __ 和 -- 做正则匹配。写错就等于关掉整套防护。
-
.card-header--large❌(漏一个_,会被误认为普通类名,可能泄漏到非 BEM 区域) -
.button__icon_error❌(用单_代替--,stylelint-selector-bem-pattern直接报错) -
.card__body--compact--dark❌(修饰符不能嵌套,应拆成两个独立类:.card__body--compact .card__body--dark)
配置 stylelint-selector-bem-pattern 并设 {"styleType": "bem"},CI 中跑 npx stylelint "**/*.{css,scss}" 可阻断这类提交。
如何避免在 SCSS 中意外写出后代选择器
SCSS 嵌套本身不违反 BEM,但一不留神就会生成破坏封装的选择器,比如 .user-card { .avatar { } } 编译出 .user-card .avatar,既无法反推来源,又让样式强依赖 DOM 层级。
- 只允许单层嵌套:
.user-card { &__avatar {} &--expanded {} } - 禁止出现带空格的嵌套:
&__avatar { &__icon {} }❌(会产出.user-card__avatar .user-card__avatar__icon,BEM 不支持三级) - 遇到“子元素的子元素”,优先拆成新 Block:
.badge--in-table比.table-cell__content__badge更可控
JS 动态拼接类名时最常踩的坑
手拼字符串看着快,实际极易在 CI 环境下因空格、连字符、大小写漏写而挂掉。本地开发跑得通,Linux 构建直接报错,且审查 DOM 时无法一眼识别归属。
-
className={`button button--${variant} ${hasIcon ? 'button__icon' : ''}`❌(缺空格、button__icon脱离 block、无防错) - 推荐封装生成函数:
const BLOCK = 'search-form'; const cn = (e, m) => `${BLOCK}${e ? '__' + e : ''}${m ? '--' + m : ''}`,调用cn('input', 'disabled')得到search-form__input--disabled - React 中用
clsx处理组合:className={clsx('search-form__input', { 'search-form__input--disabled': disabled })}
第三方组件怎么套 BEM 而不破坏升级路径
Ant Design、react-datepicker 这类库的类名你无法控制,硬改 .ant-btn 成 form__ant-btn 会破坏封装和后续升级。正确做法是用 wrapper block 隔离作用域。
- 包一层自有 Block:
<div class="form-field--date"><datepicker></datepicker></div> - CSS 中只对 wrapper 写样式:
.form-field--date .ant-date-picker✅(允许有限度的后代选择器,仅用于包裹第三方) - 禁用
!important:它会截断 BEM 的 modifier 覆盖链,比如button--primary本该覆盖基础色,却被外部!important拦住
真正难的不是写对第一个 __,而是每次写 class 前都问一句:“它属于谁?会不会在其他上下文生效?”——这个习惯比任何工具都关键。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











