全局工具类必须加语义化前缀(如u-、is-、js-),禁止裸名;BEM组件内禁用裸工具类,第三方库需限定作用域并加前缀;工具类与modifier应职责分离,避免共存冲突。

全局工具类命名必须带明确前缀,不能裸名
像 text-center、mt-2 这类工具类一旦不加前缀,就会和 BEM 的 card__title 或 form__input 在同一作用域竞争——浏览器不区分“谁是工具、谁是组件”,只认类名字符串。你写个 flex,它就可能意外覆盖掉某个 block 里用到的 card__flex-container(如果真有人这么命名)。
常见错误现象:u-text-center 被写成 text-center,结果在 profile-card__content 里加了这个类,反而把卡片内所有子元素居中了;或者第三方 UI 库自带的 ant-col 和你手写的 col 冲突,导致栅格错乱。
- 必须统一用语义化前缀,如
u-(utility)、is-(state)、js-(JS hook),禁止裸名hidden、active - 前缀需与 BEM block 名完全不重叠:避免用
card-作工具类前缀,否则card-hidden容易被误认为是cardblock 的 modifier - 构建阶段可用 PostCSS 插件自动补前缀,比如将
text-center→u-text-center,避免人工遗漏
BEM 组件内部禁用裸工具类,只允许前缀化或封装后使用
在 search-form__input 这类元素上直接写 class="search-form__input text-sm" 是高危操作——text-sm 不属于该 block,也不受其控制,未来某天全局 text-sm 改了 line-height,所有用了它的组件都会悄悄变形。
真实项目里最容易出问题的是表单字段、弹窗遮罩、加载状态这些高频复用区域,一不留神就混入裸工具类。
- 组件模板中只允许出现本 block 的类(
search-form__input)或带前缀的工具类(u-text-sm) - 禁止在 SCSS 中用
@extend .u-text-sm—— 这会把工具类样式注入到组件 CSS 里,破坏隔离性;应保持工具类纯原子、无依赖 - 若需组合多个工具效果,用封装函数生成完整类名:
cn('search-form__input', 'u-text-sm u-mb-1'),而非手动拼接
第三方工具类库(如 Tailwind)必须限定作用域
Tailwind 默认生成的 px-4、bg-blue-500 类名毫无上下文,直接引入等于给全局开了个口子。哪怕你整个项目都用 BEM,只要有一个 div class="px-4 bg-blue-500",它就能穿透所有 block 边界生效。
这不是 Tailwind 的问题,是你没控制它的作用域边界。很多团队上线后才发现按钮颜色被某处 bg-gray-200 意外覆盖,查了半天发现是文档页里一个 pre 标签偷偷引入了全局工具类。
- 通过
prefix配置项强制加前缀:prefix: 'tw-'→ 生成tw-px-4,避免和业务类名冲突 - 禁用
@layer base中的全局重置(如*, ::before, ::after),改用 BEM block 自己定义 reset,例如card__reset - 对已存在的第三方工具类,用属性选择器收束影响范围:
.myapp-root [class^="tw-"],而不是放任.tw-px-4匹配全站任意元素
工具类与 BEM modifier 共存时,优先用 modifier 表达业务状态
看到 button--disabled 和 u-disabled 同时存在,大概率说明设计意图混乱:--disabled 是 button 自身的状态,u-disabled 却是通用视觉禁用,两者语义不同、维护主体不同、生效时机也不同。
最典型的坑是:你在 JS 里同时切换 button--disabled 和 u-opacity-50,结果 CSS 优先级打架,禁用态要么不生效、要么透明度叠加过深。
- modifier 只表达业务逻辑状态:
button--loading、form--submitted,由组件自身控制 - 工具类只负责纯视觉原子能力:
u-opacity-50、u-pointer-events-none,但不应替代业务状态 - 真需要复用视觉效果,把工具类变量注入 BEM 结构:
.button--disabled { opacity: var(--u-opacity-50); },而不是让两个类名在 DOM 上共存
BEM 本身不阻止你写 class="card__header u-text-lg",但这种写法会让 u-text-lg 的变更风险扩散到所有用到它的组件。真正难的不是命名规则,而是每次加类时多问一句:这个类的生命周期,是否和当前 block 绑定?
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











