样式没生效主因是特异性(specificity)不足而非书写错误;需用开发者工具检查Styles面板中带删除线的规则及Computed面板生效来源,优先提升选择器特异性而非滥用!important。

样式写了但没生效,大概率不是写错了,而是被其他规则“压”住了——specificity(特异性)才是决定谁赢的裁判,不是书写顺序,也不是类名“看起来更合理”。
怎么看哪个CSS规则实际生效了
打开浏览器开发者工具(F12),选中目标元素,在右侧 Styles 面板里找带删除线(strikethrough)的样式声明。那些就是被更高特异性规则覆盖掉的。鼠标悬停在被划掉的属性上,会显示 specificity is lower than another rule,并附带一个四元组值,比如 0,1,1,0:
- ID 选择器(如
#header)贡献 100 → 对应四元组第二位 - 类 / 属性 / 伪类(如
.btn、[disabled]、:hover)各贡献 10 → 第三位 - 标签 / 伪元素(如
div、::before)各贡献 1 → 第四位 - 内联样式(
style="...")是 1000 → 第一位,基本不可逆
注意:!important 不改变这个四元组值,它只是让该声明跳过层叠顺序,直接比权重;如果两个都带 !important,还是得回退到比这个四元组。
为什么加了类名还是不生效
常见错觉:你写了 .card__title,但页面同时存在一条 .modal .header h2 —— 后者特异性是 0,0,2,1(两个类 + 一个标签),而前者只有 0,0,1,0,直接被盖掉。
立即学习“前端免费学习笔记(深入)”;
- 第三方库(如 Ant Design、Bootstrap)常在末尾引入,且用深度嵌套或 ID,你写的
.btn很难赢过它的.ant-btn-primary -
.card .title和.card > .title是两回事:前者匹配任意后代,后者只认直接子元素 - Vue 的
<style scoped>或 React 的 CSS Modules 会自动加属性选择器(如div[data-v-abc123]),你写的div .title根本匹配不到真实 DOM - 别靠“看起来像”判断匹配,右键审查元素,确认
class名拼写、空格、连字符完全一致
怎么安全地提高选择器特异性
优先用精准选择器,而不是堆砌或硬加 !important。后者短期解急,长期会让后续修改变成“谁最后加 !important 谁赢”的混乱局面。
- 改用 BEM 命名(如
.card__title--large)替代无意义嵌套(.card .content .title),权重稳定且语义清晰 - 用子选择器
>替代空格,例如.dialog > .footer比.dialog .footer更可控 - 必要时用属性选择器兜底:
[data-role="tooltip"]权重 = 10,和类持平,但更不易被意外覆盖 - 避免
@import:它解析滞后,容易打乱预期的加载顺序;多个<link>按 HTML 出现顺序加载,把自定义样式放最后
CSS Modules 才是真正防冲突的落地方案
BEM 再规范,编译后仍是全局类名;两个组件都写了 .button,后加载的就会覆盖前一个。真正有效的解法是构建时隔离。
- Webpack/Vite 默认支持
.module.css,所有类名自动哈希,比如.btn编译成Button_btn__Kx2f1,天然不冲突 - 必须用 JS 对象方式绑定:
className={styles.btn},硬写class="btn"会失效 - 要覆盖第三方组件(如 antd),得用
:global(.ant-btn)显式声明局部作用域内的全局选择器 - 最常被忽略的是构建配置细节:BEM 写得再规范,如果没启用 CSS Modules 或没配对哈希规则,它就只是语义更清晰的裸奔代码
真正难的不是记住规则,而是每次写 class 前问一句:“这个样式属于谁?它会不会被另一个同名类悄悄覆盖?”


















