bem规范禁止id选择器参与css匹配,因其权重1,0,0碾压类名0,1,0,导致覆盖失效、复用困难、协作对抗;id仅可用于js锚点或语义标识,样式必须由bem类名驱动。

id必须唯一,class可以复用是硬性规范,不是风格偏好
浏览器不报错,但重复 id 会让 document.getElementById() 只返回第一个匹配元素,label[for]、锚点跳转(#section2)、aria-labelledby 全部失效。这不是“可能出问题”,而是只要重复,这些机制就必然不可靠。
常见错误场景:
- Vue/React 组件循环渲染时写死
id="modal" - 复制 HTML 片段后忘记改
id值 - 服务端渲染与客户端 hydrate 时 id 生成逻辑不一致
验证方式:在控制台运行 document.querySelectorAll('[id="xxx"]'),若长度大于 1,立刻修复。
class 名不能描述样式,而要表达语义或功能
写 class="red-text" 或 class="float-left" 看似省事,实际埋下维护雷:设计改版后颜色/布局变了,但 class 名没变,CSS 里就得全局搜替换,还容易漏掉嵌套结构里的同名 class。
正确做法:
- ✅ 用
error-message、search-submit、nav-main—— 表达它“是什么” - ❌ 避免
big-title、clear-fix(除非是明确约定的工具类,如u-clear) - 工具类统一加
u-前缀:u-hidden、u-text-center
kebab-case(短横线)是唯一全链路安全的分隔方式;下划线在旧 IE 有兼容问题,驼峰易与 JS 变量混淆,中文或拼音会导致 Git diff 乱码、Webpack 编码警告。
#header 比 .header 更难覆盖,特异性失控是真实痛点
#header 的 CSS 权重是 0,1,0,0,.header 是 0,0,1,0。这意味着哪怕你写十个连写的 class 选择器,也压不过一个 #header 的声明。一旦第三方库或后续模块引入了 #header 样式,你就只能靠 !important 或更高权重选择器硬覆盖——这直接破坏样式可预测性。
更现实的问题:
- BEM 类名如
card__title可安全嵌套、组合、条件添加,id不行 - 组件化开发中,
id无法通过 props 动态生成又保证唯一,class则天然支持 - 测试时模拟点击
label[for="email"]依赖id正确绑定,但样式调试却常因id权重失控而失败
BEM 不是语法强迫症,而是结构意识的落地方式
BEM 的价值不在双下划线或双短横这些符号本身,而在逼你思考「块(block)→ 元素(element)→ 修饰符(modifier)」的层级关系。比如 card 是块,card__title 是元素,card--hover 是修饰符。
好处很实在:
- 避免样式污染:
.card__title不会意外影响其他.title - 状态明确:
button--disabled比button.disabled更易维护 - 工具类隔离:
u-hidden和card--hover职责分明,不会混用
注意:card__header--highlighted 这种写法主干仍是 kebab-case,BEM 符号只是语法糖,核心是结构意识——没它也能写好 class,但有了它,多人协作和组件迭代时少踩一半坑。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











