kebab-case 是唯一全链路兼容的分隔方式,因其在html解析、css选择器、构建工具、bem协议及微前端中均无兼容性问题,且与vue/react组件命名天然对齐。

kebab-case 是唯一全链路兼容的分隔方式
浏览器解析 HTML 时,class 属性值不区分大小写,但对特殊字符敏感;下划线(_)在旧版 IE 和部分 SSR 框架中可能被截断或转义失败;驼峰(如 userAvatar)在 CSS 选择器中是非法标识符,必须用反斜杠转义才能使用,实际几乎没人这么写。
短横线(-)是 HTML5 原生支持、无需转义、阅读清晰、Git diff 友好、Webpack 构建无警告的唯一安全选项。所有主流构建工具、Linter、CSS-in-JS 库默认都只校验 kebab-case 格式。
为什么 BEM 的 __ 和 -- 必须配合 kebab-case 使用
BEM 的三段式结构 block__element--modifier 本质是一套语义协议,不是字符串拼接游戏。一旦混入驼峰或下划线,就破坏了“单类名 = 单次哈希查找”的前提:
-
userCard__avatar--loading:CSS 解析器会当作一个整体类名,但部分浏览器或预处理器可能误判为两个 token(userCard__avatar和--loading),导致匹配失败 -
user_card__avatar--loading:下划线在某些 SSR 模板引擎(如 Thymeleaf、Jinja2)中会被过滤或转义,最终生成的 class 可能变成usercard__avatar--loading -
user-card__avatar--loading:合法、可预测、DevTools 里一眼可查,且与 Vue 组件名user-profile-card.vue、文件路径/components/user-profile-card/天然对齐
kebab-case 如何影响 BEM 工具链校验
没有统一格式,stylelint-selector-bem-pattern 这类插件就失去意义。它依赖正则严格匹配 [a-z][a-zA-Z0-9]+(__[a-z][a-zA-Z0-9]+)?(--[a-z][a-zA-Z0-9]+)? 这类规则——任何大写字母、下划线、数字开头都会被拦截。
常见失效场景包括:
- 手写
ProductCard__header→ 被stylelint报错Expected selector to match specified BEM pattern - JS 中动态拼接
className={`user-card__avatar ${isSmall ? 'user-card__avatar--sm' : ''}`→ 如果写成userCard__avatar--sm,样式根本不会生效 - 后端模板直出
class="user-card__avatar--xs"→ 若前端 JS 试图用el.classList.contains('userCard__avatar--xs')判断,永远返回false
Vue/React 组件边界与 kebab-case 的硬性绑定
Vue 模板中使用自定义组件标签(如 <user-profile-card></user-profile-card>)时,HTML 解析器会强制将标签名转为小写;React JSX 编译后也要求 DOM 标签名符合 HTML 规范。这意味着:
- 组件名
UserProfileCard在 DOM 中变成userprofilecard,无法匹配user-profile-card类名 - 但 CSS 类名
user-profile-card__avatar和组件文件名user-profile-card.vue、目录名user-profile-card/完全一致,新人看类名就能定位到源码位置 - 微前端子应用输出的
payment-form__submit--pending不会和主应用的search-form__submit--pending冲突,命名空间天然隔离
真正难的不是记住规则,而是每次敲下 __ 或 -- 时,确认前后都是小写字母加短横线——少一个连字符、多一个大写,整个 BEM 链条就断在那一点上。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











