ESLint本身不校验CSS类名,因其仅将className视为普通字符串而不解析结构;需借助eslint-plugin-css-modules等插件,结合正则与模块化约束,才能实现BEM等规范的静态校验。

为什么 ESLint 本身不校验 CSS 类名
ESLint 是 JavaScript/TypeScript 代码检查器,className 字符串里的类名(如 "user-card__avatar--large")对它来说只是普通字符串,不会被解析为 BEM 结构。你写 className="btn__icon btn--primary" 或 className="Button-icon",ESLint 默认完全无感——除非你告诉它“这段字符串要按 BEM 规则验证”。
必须用 eslint-plugin-css-modules + 自定义规则
在 React/Vue/Svelte 单文件组件中校验 className 是否符合 BEM,核心是两步:
- 安装插件:
npm install --save-dev eslint-plugin-css-modules - 在
.eslintrc.cjs中启用并配置规则,重点是css-modules/no-invalid-classname和自定义正则
示例配置片段(适配 user-card.module.scss):
rules: {
'css-modules/no-invalid-classname': ['error', {
'allowedPattern': '^user-card(__[a-z][a-zA-Z0-9]*)?(--[a-z][a-zA-Z0-9]*)?$',
'disallowPartialMatches': true
}]
}
这个正则强制:只允许以 user-card 开头,后面可跟 __xxx 或 --xxx,且不允许 user-card__title--large 以外的任意组合(比如 user-card__title__icon 会报错)。
React 中 className 拼接是最大漏检点
以下写法会让所有 BEM 校验失效:
-
className={`user-card ${isActive ? 'user-card--active' : ''}`}—— ESLint 只看到字符串模板,无法静态分析分支结果 -
className={styles['user-card__icon']}—— 如果styles来自非本模块的 CSS Modules 文件,插件默认不跨文件校验 -
className={clsx('user-card', `user-card__${type}`)}—— 动态拼接部分脱离正则约束
正确做法是:只允许显式字面量、且全部来自当前模块的 styles 对象键名。例如:
import styles from './UserCard.module.scss';
// ✅ 安全:key 必须在 UserCard.module.scss 中声明过
<div classname="{styles['user-card']}"></div>
@@##@@
Vue/Svelte 的
ESLint 默认跳过 内容,但 BEM 类名定义源头就在那里。要让校验闭环,必须:
- 确保
stylelint-selector-bem-pattern已配置好preset、componentName、ignoreSelectors三项(否则.btn-primary这类会漏检) - 用
eslint-plugin-vue或eslint-plugin-svelte的no-unused-vars类规则,配合vue/compiler-sfc解析器,把中声明的类名提取出来,和中实际使用的做比对 - 禁止在
中硬编码类名:→ 必须用:class="styles['user-card__title']"(SFC 场景)没这层联动,
里写了.user-card__title,但模板里用了user-card-title,ESLint 就永远发现不了。BEM 校验真正卡住问题的不是正则多严,而是能否把「定义处」「引用处」「拼接处」三端全部纳入同一套规则引擎——漏掉任意一环,就等于给违规开后门。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











