直接写 .button 必然导致样式污染,因为css无模块化概念,浏览器只匹配选择器,所有.button规则无论来源均叠加生效;bem通过block__element--modifier结构将作用域编码进类名,使search-form__input天然声明归属、角色与状态,实现语义隔离。

为什么直接写 .button 必然导致样式污染
浏览器根本不识别“模块”或“组件”,只看选择器是否匹配 DOM。只要页面里存在 <button class="button"></button>,所有定义过 .button 的 CSS 规则——无论来自你写的 welcome.css、第三方 antd.css,还是构建时拼进去的 legacy-theme.css——都会叠加上去。这不是加载顺序问题,是作用域缺失:CSS 从诞生起就没有模块化概念。
search-form__input 不是加长名字,是声明作用域边界
BEM 的三段式不是为了好看,而是让类名自带归属信息:search-form 是 block(功能闭环单元),__input 表示它只能作为该 block 的直属子元素存在,--disabled 描述状态变体。这个类名本身就在回答:“它属于谁?它是什么角色?它现在什么状态?”
PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。
- 删掉父级
search-form类,search-form__input样式就完全失效——这才是真正的隔离 - 禁止写
.search-form .input这类后代选择器,那等于主动凿穿沙箱墙 - 不允许跨 block 复用:哪怕视觉一样,
.header__button和.modal__button也必须分开命名
为什么 BEM 类名还会冲突?关键在工具链没跟上
BEM 本身不生成唯一类名,它只是约定。两个开发者都写了 .user-card__avatar,后加载的规则就会覆盖前一个——这仍是 CSS 层叠机制决定的。真正防住冲突的不是人写对了,而是工具守住边界:
- 用
stylelint-selector-bem-pattern禁止出现.btn、.input这类裸名 - 配置
postcss-bem-linter白名单,限定 block 只能是['button', 'card', 'search-form'] - JS 拼类名必须封装,比如
cn('input', 'disabled'),而不是手写button__icon缺空格或漏双下划线
第三方组件和动态 DOM 怎么不破 BEM 沙箱
BEM 不拦截外部代码,但你可以框住它的影响范围。重点不是“不让它进来”,而是“不让它乱跑”:
- 给
<el-date-picker></el-date-picker>包一层<div class="form-field--date">,然后所有定制样式都限定为 <code>.form-field--date .el-input__inner - 禁用全局属性选择器如
[class^="ant-"],必须加外层限定:.myapp-ui [class^="ant-"] - 服务端渲染的静态 HTML 若无法改 class,就在外层容器加 BEM 块名,再用属性选择器兜底:
.article-content [class^="hljs-"]
最常被忽略的一点:BEM 沙箱的完整性不取决于你写了多少
__ 和 --,而取决于每次写 class 前,你有没有真问过:“这个样式属于谁?它会不会在别的上下文里意外生效?”










