css样式污染本质是选择器作用域过宽,bem通过block__element--modifier命名强制语义归属实现隔离,虽不依赖构建工具但需团队严格遵守规范。

为什么全局CSS类名会互相覆盖
样式污染本质是CSS选择器作用域太宽。浏览器不区分“谁写的样式”,只看选择器权重和加载顺序。.btn 在 header 里定义一次,footer 里再写一次,后加载的就生效——没人管你是不是想复用还是想隔离。
- 多人协作时,
.list、.item 这类泛化名几乎必然冲突
- 组件库和业务代码共存时,
.modal 可能同时被 antd 和你自己的弹窗使用
- 构建工具(如 Webpack)不做 CSS 模块化处理时,所有
style 标签都进全局作用域
BEM命名怎么避免类名撞车
BEM 把类名拆成 block__element--modifier 三层结构,靠前缀强制绑定语义归属。不是约定,是命名即作用域。
-
search-form 是 block,代表独立功能模块
-
search-form__input 是 element,只能属于 search-form
-
search-form--compact 是 modifier,描述状态,不单独存在
- 禁止跨 block 使用 element,比如不能给
user-card<strong>avatar</strong> 加 search-forminput 的样式
.list、.item 这类泛化名几乎必然冲突 .modal 可能同时被 antd 和你自己的弹窗使用 style 标签都进全局作用域 block__element--modifier 三层结构,靠前缀强制绑定语义归属。不是约定,是命名即作用域。
-
search-form是 block,代表独立功能模块 -
search-form__input是 element,只能属于search-form -
search-form--compact是 modifier,描述状态,不单独存在 - 禁止跨 block 使用 element,比如不能给
user-card<strong>avatar</strong>加search-forminput的样式
示例对比:
污染写法:.input { border: 1px solid #ccc; }
安全写法:.search-form__input { border: 1px solid #ccc; }
实际项目中 BEM 的落地要点
BEM 不是加个横线就完事,关键在约束力和可维护性。
- 所有 class 名必须从设计稿/需求文档的模块粒度出发,一个视觉区块对应一个
block
- element 层级最多到二级,禁止
block<strong>elem</strong>subelem —— 那说明 block 划分过粗,该拆组件
- modifier 必须配合 block 或 element 使用,单独写
.button--primary 是错的,得是 .button--primary 或 .button__text--primary
- 工具链建议:VS Code 装
BEM Helper 插件,自动补全;CI 中加 ESLint 规则校验 class 命名格式
和 CSS Modules、scoped style 比有什么区别
BEM 是纯命名规范,不依赖构建工具,但也不提供运行时隔离。
-
CSS Modules 会把 .button 编译成 _button_abc123,彻底避免冲突,但需要 loader 支持,且动态拼接 class 名容易出错(比如 cls + '__icon' 写成 cls + '-icon')
-
<style scoped></style>(Vue)靠属性选择器模拟作用域,但父子组件通信时样式穿透难控制,且对 :deep() / :global() 的使用边界容易模糊
- BEM 兼容性最好,IE8 都能跑,但全靠人守规矩;一旦有人写
.header .logo 这种上下文选择器,就等于开了后门
block block<strong>elem</strong>subelem —— 那说明 block 划分过粗,该拆组件 .button--primary 是错的,得是 .button--primary 或 .button__text--primary BEM Helper 插件,自动补全;CI 中加 ESLint 规则校验 class 命名格式 -
CSS Modules会把.button编译成_button_abc123,彻底避免冲突,但需要 loader 支持,且动态拼接 class 名容易出错(比如cls + '__icon'写成cls + '-icon') -
<style scoped></style>(Vue)靠属性选择器模拟作用域,但父子组件通信时样式穿透难控制,且对:deep()/:global()的使用边界容易模糊 - BEM 兼容性最好,IE8 都能跑,但全靠人守规矩;一旦有人写
.header .logo这种上下文选择器,就等于开了后门
真正麻烦的不是命名本身,是团队里总有人觉得 “就改一行样式,没必要套三层”。等上线后发现 .card<strong>title</strong> 被另一处 .articletitle 的 margin-top 带偏了,才回去翻 commit 记录——这种问题不会报错,只会静默错位。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











