因为CSS-in-JS解决样式隔离但不解决语义表达,BEM类名(如card__header)作为人类可读锚点,支撑调试、搜索、跨角色协作与工具链校验,是人机共读的接口协议。

为什么 CSS-in-JS 项目里还总看到 card__header 这类类名?
因为 CSS-in-JS 解决了作用域隔离,但没解决语义表达。你用 styled.div 包一层,生成的哈希类名(如 sc-bdVaJa)对机器友好,对人完全不可读——调试时 DevTools 里看到 sc-bdVaJa,根本猜不出它对应哪个组件、是标题还是操作区、处于什么状态。
保留 BEM 类名(哪怕只是作为 className 显式传入),本质是在运行时留一个“人类可读锚点”。比如:
const CardHeader = styled.h2`...`; <cardheader classname="card__header--large">...</cardheader>
——这时类名不参与样式计算,但能让 grep -r "card__header" src/ 瞬间定位到组件定义,也方便后端模板、埋点系统或 SEO 工具识别结构。
props.variant 和 --primary 修饰符到底是什么关系?
CSS-in-JS 的 props(如 variant="primary"、size="large")不是随意起的,它们是 BEM 修饰符逻辑的接口映射:
-
button--primary→,底层生成的样式逻辑仍需区分基础态与变体态 -
card--fluid→,而非写成这种布尔裸露写法 - 键值型修饰符(如
--density-compact)对应density="compact",比isCompact更易扩展多态
绕开这层映射,直接用 if (props.primary) {...} 拼样式,很快会陷入状态组合爆炸,且无法被构建工具(如 PostCSS 插件)统一提取或校验。
为什么 stylelint-selector-bem-pattern 在 CSS-in-JS 里依然要设为 error?
很多人以为用了 styled-components 就不用管 BEM 规则了,结果写出:
const Avatar = styled.img`
&.user-card__avatar { ... } /* 错:空格选择器 */
&__icon { ... } /* 错:伪 BEM,实际编译成 .sc-xxx__icon */
`;
这类写法看似“用了 BEM”,实则破坏了两个关键契约:
- 空格选择器(
.user-card__avatar)在 SSR 直出 HTML 时生效,但 JS 加载后可能被覆盖;微前端子应用若未加载该组件 JS,样式直接失效 -
&__icon这种嵌套语法依赖编译器实现,不同库行为不一致(Emotion 支持,styled-components v6+ 需额外配置),且无法被 CI 工具静态扫描
真正该 lint 的,是最终输出的 class 字符串是否符合 BEM 语义——哪怕它只出现在 cx() 或 className 中,也要确保 user-card__avatar--large 合法,而 user-card__avatar-large 或 avatar--large 被拦截。
最常被忽略的一点:BEM 不是写给 CSS 看的,是写给协作链路上所有人看的
一个 product-card__price--discount 类名,同时服务于:
- 前端开发者:一眼看出这是商品卡片的价格模块,且处于折扣状态
- 后端工程师:在 Java/PHP 模板里写 无需查文档
- Figma 设计师:导出代码时能匹配到真实类名,避免手动改写
- SEO 工具:识别语义区块,辅助内容结构化
而
css\`color: red\`或tw\`text-red-500\`只对当前文件有效,跨角色、跨技术栈时完全失语。BEM 思想在这里不是命名规则,而是接口协议——它不阻止你用 CSS-in-JS,但要求你在协议层保持可交换性。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











