命名应采用“前缀+功能+状态”组合(如user-profile-card),避免通用名冲突;scoped样式对动态DOM和第三方slot无效,需手动加唯一class或用CSS-in-JS;后代选择器易失控,推荐单一class或一级BEM嵌套;媒体查询和伪类须包裹在组件class内以防全局污染。

class 名怎么起才不容易撞车
直接用 header、button、list 这类通用名,几乎必然冲突——尤其在多人协作或引入第三方 UI 库时。前缀 + 功能 + 状态 是更稳妥的组合方式,比如 user-profile-card、cart-item-delete-btn、modal-overlay--hidden。
注意两点:
• 不要用 btn-primary 这种纯语义名(它描述的是“谁用”,不是“这是什么”);
• 双中划线 -- 表示修饰状态,单下划线 _ 少用(容易和 BEM 的修饰符混淆);
• 组件内嵌子元素用双冒号 __,如 user-profile__avatar,明确层级归属。
为什么 scoped 样式在 Vue 里不一定够用
scoped 只给当前组件的 加属性选择器,但对动态插入的 DOM(比如 el-dialog、Popover、Toast)或第三方库的 slot 内容无效。这些内容仍会暴露在全局作用域下。
实际应对方法:
• 对第三方弹层容器手动加唯一 class,再写样式限定范围,如 .my-app .el-dialog__body { padding: 16px; };
• 使用 CSS-in-JS 方案(如 styled-components 或 vue-styled)时,确保插值生成的 class 真实隔离;
• 避免用 !important 覆盖第三方样式——它会破坏可维护性,且可能被更高权重的选择器反杀。
立即学习“前端免费学习笔记(深入)”;
后代选择器 vs 层级嵌套:哪个更容易失控
写成 .card .title .icon 看似清晰,但只要中间某一层 class 改名或结构微调,整条链就断;更危险的是,它隐含了深度依赖,别人改个 div 套一层,样式就失效或误匹配。
推荐做法:
• 单一 class 控制单一视觉单元,如 card-title-icon;
• 必须嵌套时,只限一级,如 .card__title 和 .card__title-icon,不跨三级;
• 利用 :where() 或 :is() 降低权重(例如 :where(.card) .title 权重恒为 0-1-0),但注意 IE 和旧 Safari 不支持;
• 禁止用 div.card div.title span 这类基于标签的选择器——HTML 结构一变全废。
伪类和响应式断点如何避免污染全局
:hover、:focus-visible、@media 本身不产生新 class,但若写在顶层(没包裹在组件 class 下),就会作用到所有匹配元素。常见错误是:@media (max-width: 768px) { .header { display: none; } } ——这个 .header 若在别处也用了,就一起消失了。
安全写法:
• 所有媒体查询必须嵌套在组件 class 内,如 .user-profile { @media (max-width: 768px) { flex-direction: column; } };
• 伪类统一用 BEM 修饰符表达,如 .btn--disabled:hover,而不是单独写 .btn:hover;
• 避免在 CSS 文件顶部写全局 :root 媒体查询变量——不同组件对「移动端」的理解可能不一致,应由组件自己决定断点行为。
真正难防的不是命名重复,而是样式意图模糊带来的隐式耦合——比如一个 margin-bottom 看似只是间距,实则承担着「分隔区块」的布局职责;换地方复用时,它可能把不该撑开的地方也撑开了。










