BEM在Hybrid中是保命线而非加分项,类名必须原样存在于DOM:禁用scoped/cssModules、禁动态拼接、禁后代选择器,确保user-card__title不被改写、不被宿主样式命中、不因修饰符错位而失效。

移动端 H5 在 Hybrid 容器里用 BEM,不是写不写的问题,而是写错就直接失效——类名被构建工具改写、被宿主样式命中、被 JS 拼错,三者任一发生,user-card__title 就等于没写。
类名被构建工具哈希或属性包裹,BEM 彻底失效
uni-app 开启 scoped、Taro 启用 cssModules、Vite 插件自动加 data-v-xxx,都会让 user-card__title 变成 user-card__title[data-v-abc123] 或 styles["user-card__title_abc"]。宿主 WebView 里的 JS 脚本和注入样式根本匹配不到这个类名,!important 都救不了。
- uni-app 必须关闭
<style scoped></style>,改用普通<style></style> - Taro 项目检查
config/index.js中是否设置了cssModules: true,关掉 - 打开 Chrome DevTools 的 Elements 面板,确认 DOM 中的
class属性值是原样字符串,不是空、不是驼峰、没带data-后缀
动态拼接 class 名,语义断裂且易 XSS
写 el.className = 'user-card__' + type + '--' + state 看似灵活,实则踩中三颗雷:JS 字符串拼错(漏空格、多连字符)、WebView 下 CSSOM 不触发重绘、拼出的类名无法被 stylelint 或 IDE 识别。
- 禁止在 JS 中用字符串拼接生成 BEM 类名
- 所有类名必须显式写死在模板中:
<div class="user-card user-card--active"> <li>状态切换用 modifier 控制,比如 <code>user-card--loading,而不是靠 JS 插入user-card__content--loading - Modifier 必须和对应 Block 同时存在:
class="button button--disabled"✅,class="button--disabled"❌ - Element 不该有自己的 Modifier:
input__label--error应改为input--has-error或input--invalid - 避免泛义词:
--primary改为--submit、--cancel,明确业务意图 - BEM 选择器必须扁平:
.user-card__title✅,.user-card .user-card__title❌ - SCSS 只允许单层嵌套:
&__title和&--active可以,&__title { &__icon { } }不行 - “子元素的子元素”应拆成新 block:
table-cell__badge→ 改为badge badge--in-table-cell
修饰符挂错位置,破坏状态封装
card__title--large 是典型错误:尺寸变化通常影响整个卡片布局,不该只作用于标题元素;button--disabled 单独使用也危险——删掉它后基础样式可能直接崩塌,因为没绑定到 button 这个 block 上。
立即学习“前端免费学习笔记(深入)”;
用后代选择器模拟嵌套,性能与维护双崩
写 .user-card .user-card__title 或 SCSS 里 .user-card { &__title { &__icon { } } },会生成非法的 .user-card__title .user-card__title__icon,既违背 BEM 原则,又在低端 Android WebView 里触发 CSSOM 回溯匹配,列表滚动卡顿明显。
真正卡住团队的从来不是规则多难记,而是每次敲下 class=" 之前,没问一句:这个样式属于哪个独立功能单元?它会不会在别的页面、别的宿主环境里意外生效?


















