BEM严禁使用ID选择器因其特异性过高(100)导致样式难以覆盖,且ID违背组件唯一性原则,易在SPA、SSR、微前端中引发冲突、DOM异常、事件错乱及测试不稳定;替代方案为data属性或ARIA属性,JS绑定优先用ref。
#header这类ID选择器因为ID选择器的CSS特异性(specificity)是100,远高于class(10),一旦用#nav写样式,后续几乎无法用常规class覆盖——哪怕加!important也治标不治本。BEM强调组件可复用,而ID天然违背“唯一性”前提:SPA里多个router-view可能同时渲染同名模块,id="user-card"必然冲突。
.UserCard和#user-card在真实项目中的表现差异实际开发中,ID选择器会立刻暴露三个硬伤:
id重复触发HTML校验失败,控制台报DOMException: Failed to execute 'getElementById' on 'Document'
document.getElementById()取元素,但BEM组件常需动态挂载/卸载,ID残留导致事件绑定错乱或内存泄漏.UserCard__avatar始终语义明确、定位精准不用ID,不等于放弃语义化锚点。可行路径有且仅有两种:
<div class="UserProfile" data-user-id="123">,CSS中用[data-user-id]做轻量区分,特异性仅11,不影响覆盖链aria-labelledby或aria-owns满足无障碍需求,它们不参与样式计算,也不破坏BEM的class-only原则ref(Vue)或useRef(React),而非document.getElementById
很多团队写了.Button--primary却仍偷偷在HTML里留着id="submit-btn",只因“方便调试”。但这个口子一开,新成员会默认ID可用,接着出现#Button--primary这种非法组合——BEM的class-only约束是原子级的,容不得半点妥协。真正省事的做法,是在ESLint插件stylelint-selector-bem-pattern里强制校验,把#字符直接拦在提交前。
立即学习“前端免费学习笔记(深入)”;