.bem 命名规范通过 block__element--modifier 结构确保样式解耦,防止全局冲突;需配合 stylelint-selector-bem-pattern 校验语法、函数封装 classname、css modules 及 @scope 注释强化作用域控制。

为什么 .btn 一合并就出事,而 .search-form__submit 不会
浏览器不认“这是登录页的按钮”,只按选择器权重和加载顺序匹配。.btn 这种泛义名在多个文件里重复定义,后加载的 CSS 直接覆盖前一个,没人能预判哪条生效。.search-form__submit 天然绑定到搜索表单模块,哪怕另一个模块也写了同名规则,只要它不在同一作用域(比如不同 CSS 文件、构建时未混入),就不会互相干扰。
-
.btn可能被 A 组用在弹窗、B 组用在表单、C 组用在侧边栏,最终打包成一份 CSS 后,三处按钮样式全乱 -
.search-form__submit只会在search-form/目录下出现,CI 扫描到其他地方用了这个类名,立刻报错 - 修饰符如
--loading描述状态,不是尺寸或颜色,避免后续主题切换时全局搜20px改成1.25rem
stylelint-selector-bem-pattern 怎么真正拦住命名违规
Stylelint 本身不识别 BEM,必须靠 stylelint-selector-bem-pattern 插件校验语法结构。它只管“怎么写”,不管“在哪写”——接受 .block__element--modifier,拒绝 .block_element 或 .button--primary(缺 block 前缀)。
- 常见报错:Expected selector ".user_avatar" to match specified BEM pattern → 必须双下划线:
.user__avatar -
.button-disabled错,必须双破折号:.button--disabled - 元素名含大写字母:
.card__UserAvatar默认被拒,推荐全小写+中划线:.card__user-avatar - 嵌套过深:
.card__content__title违反 BEM,应改为.card__title或抽新 block
JS 里拼 className 是最隐蔽的冲突源
手拼字符串看着快,实际是定时炸弹:className={`button button--${variant} ${hasIcon ? 'button__icon' : ''}` 缺空格、漏连字符、button__icon 脱离 block 上下文,CI 里一跑就挂。
- ❌ 危险写法:
el.classList.add('is-loading')—— 不带 block 前缀,无法定位归属 - ✅ 推荐封装函数:
const cn = (e, m) => `${BLOCK}${e ? '__' + e : ''}${m ? '--' + m : ''}`,调用cn('input', 'disabled')得到search-form__input--disabled - 若用 CSS Modules,坚持
className={styles['search-form__input--error']},永远不硬写哈希类名 - 所有 CSS 文件开头加
/* @scope search-form */注释,CI 可扫描归属,防止某人把header.css里的规则偷偷复用到用户页
BEM 类名必须守住的三条线
BEM 不是拼下划线游戏,而是靠命名契约堵住高频漏洞。写错一个分隔符,就等于放弃解耦。
-
block__element中必须是双下划线,button-icon--large是错的——button-icon被当成新 Block,失去上下文 - element 只能属于且仅属于一个 block:
.modal__close不能同时被dialog和notification复用 - modifier 必须是布尔态或有限枚举:
product-card--on-sale合规,product-card--width-240px不合规——后者该抽成独立 element 或用 CSS 自定义属性
真正起效的不是人写得对不对,而是每次写 class 前是否真问过:“这个样式属于谁?它会不会在别的上下文里意外生效?”——这句话比任何工具配置都管用。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











