根本原因是DOM上未真实添加btn--disabled类名;需检查DevTools确认类名拼写正确(双连字符),用classList.add()而非className+=,框架中确保state驱动class更新。

JS 添加或移除 btn--disabled 时 class 没生效
根本不是 BEM 规则失效,而是 DOM 上压根没出现那个类名。浏览器只认真实写在 class 属性里的字符串。
检查 DevTools Elements 面板,确认元素是否真的包含 btn--disabled(不是 btn-disabled、btn_disabled 或漏了双连字符);
用 classList.add('btn--disabled'),别用 el.className += ' btn--disabled'——后者容易覆盖已有类或拼错空格;
React/Vue 中确保 state 变化后,className 或
/btn--(disabled|loading|error)/
。
多个状态共存时 btn--loading 和 btn--disabled 样式互相覆盖
BEM 不限制多个修饰符共存,但 CSS 层叠顺序决定最终效果——不是 class 名字写在前面就优先。
避免写组合选择器:.btn--loading.btn--disabled { ... },它权重高、难维护、且漏一个类就失效;
应分别定义基础行为:.btn--loading 控制光标、图标旋转、pointer-events: none;.btn--disabled 控制透明度、文字颜色;
若需叠加视觉反馈(比如加载中又禁用),单独写叠加规则:.btn--loading.btn--disabled { opacity: 0.5; },但仅限必要场景;
注意:不要在 .btn--disabled 里重写背景色,否则会盖掉主题变量或 .btn--primary 的颜色设定。
审查 Next.js App Router 代码库,检查 React Server Component 边界错误、请求瀑布、客户端/服务端反模式以及 Core Web Vitals 风险...
用对象映射生成 class 列表时,btn--small 和 btn--large 冲突
尺寸类属于“变体”(variant),和“状态”(state)不同,它们通常互斥——一个按钮不能同时是 small 和 large。
用对象控制时,不要把尺寸类和其他状态类平级布尔判断:const modifiers = { 'btn--small': isSmall, 'btn--large': isLarge, 'btn--loading': isLoading };
这样可能导致两个尺寸类同时存在。
更稳妥的做法是单选逻辑:const sizeMod = isLarge ? 'btn--large' : isSmall ? 'btn--small' : null;
再和状态类合并。
React 中可封装为:useButtonClasses({ size: 'large', loading: true, disabled: false }),内部做互斥归一。
切换 tab--active 后样式延迟或残留
这不是 BEM 的问题,而是 JS 更新时机和 CSS 过渡未对齐。
常见于:点击后立刻移除旧 tab--active 并添加新类,但 CSS 的 transition 还没结束,导致视觉跳变;
正确做法是等过渡结束后再清理旧状态,可用 el.addEventListener('transitionend', handler, { once: true });
或者用 getComputedStyle(el).transitionProperty 判断当前是否有过渡,动态决定是否加防抖;
更简单的方式:统一用 el.classList.replace('tab--active', 'tab--active') 替换自身,避免中间态空白期;
注意:如果用了 will-change: transform 等触发硬件加速的属性,要确保 tab--active 的样式变更不意外触发重绘链。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










