说到 CSS,很多刚入行或者写过几年代码的朋友,都会有一个共同的痛:“样式明明写了,怎么就是没生效?” 或者 “为什么我写的样式被别人的覆盖了?”
这背后其实是 CSS 引擎最核心的两个机制在打架:继承(Inheritance)和层叠(Cascading)。今天咱们不整那些干巴巴的教科书定义,我就把浏览器内部那个“裁判”给你拽出来,让它现场给你讲清楚这规则,顺便教你几招实用的排雷技巧。
一、 先搞懂“血统”:什么是继承?
想象一下,你写了一个父盒子 body,里面套了一个 div,div 里又有个 span。
<body>
<div class="container">
<p>这是一段文字</p>
</div>
</body>
body {
font-family: "Helvetica Neue", sans-serif;
color: #333;
}
这时候,div 和 p 标签虽然没写颜色,但它们会自动继承 body 的 color: #333 和 font-family。这就是继承。
但注意,不是所有属性都能继承。 这是一条铁律,你得记清楚:
- 能继承的:文本相关的(
color,font-family,font-size,line-height,text-align)、列表相关的(list-style)、光标(cursor)等。 - 不能继承的:盒模型相关的(
width,height,padding,margin,border,background)、定位(position,float)等。
为什么这很重要? 因为很多人混淆了“继承”和“层叠”。继承是从祖先传下来的,而层叠是多个规则竞争时的胜负判定。
二、 核心战场:层叠(Cascading)是怎么回事?
当两个样式规则冲突时,比如:
/* 规则A */
div { color: red; }
/* 规则B */
.container { color: blue; }
<div class="container">你好</div>
div 到底是红还是蓝?这就是层叠在起作用。
层叠其实就是一场加权打分比赛,浏览器会计算每个规则的“权重”,谁权重高谁赢。如果权重一样,后写的赢(Source Order)。
三、 选择器优先级(Specificity):权重计分表
这是解决 CSS 冲突最关键的知识点。每个选择器都有自己的“分值”,我们按以下表格来计算:
| 选择器类型 | 权重分值 | 示例 |
|---|---|---|
| 内联样式 | 1000 | <div style="color: red;"> |
| ID 选择器 | 100 | #header, #nav |
| 类/属性/伪类 | 10 | .btn, [type="text"], :hover |
| 元素/伪元素 | 1 | div, p, ::before |
| 通配符/继承 | 0 | *, body (继承来的不算分) |
举个例子,咱们算一算:
案例 1:基础比大小
/* 权重:1 (p) + 10 (.text) = 11 */
p.text { color: green; }
/* 权重:100 (#main) = 100 */
#main { color: red; }
<div id="main"><p class="text">文字</p></div>
结果:红色。因为 100 > 11。ID 选择器碾压类选择器。
案例 2:同类项叠加
/* 权重:10 + 10 = 20 */
.nav .item { color: blue; }
/* 权重:100 = 100 */
#header { color: yellow; }
结果:黄色。即使 .nav .item 写了两个类,加起来 20 分,也抵不过一个 ID。
案例 3:最坑的陷阱——继承的权重是 0!
body { color: red; } /* 继承过来的,权重 0 */
p { color: blue; } /* 直接匹配,权重 1 */
结果:蓝色。
重要提示:不要以为父元素设了颜色,子元素就一定能继承覆盖。如果子元素自身有规则(哪怕只是 color: blue),子元素的规则权重(1)永远高于父元素的继承(0)。
四、 !important:核武器,慎用!
如果你在一个样式后面加了 !important,它的优先级会超过所有普通选择器,包括 ID 和内联样式(除了其他 !important 再打架)。
div { color: red !important; }
#main { color: blue; } /* 无效 */
但是! 我强烈建议不要滥用 !important。
- 它会让代码难以维护,调试时你找不到谁在生效。
- 第三方框架(如 Bootstrap)很多内部用了
!important,你去 override 它会变成“军备竞赛”,最后满屏!important,项目直接崩溃。
什么时候用? 仅当你需要强制覆盖第三方库且无法修改其源码时,作为最后手段。
五、 实际开发中:CSS 样式不生效,怎么办?(排雷指南)
这是大家问得最多的问题。别急着骂浏览器,按这个顺序排查,99% 的问题都能解决。
1. 打开开发者工具(F12)—— 最重要的一步!
不要猜,看事实。
- 选中对应元素,看 Styles 面板。
- 被覆盖的样式会被划掉,旁边会显示是哪个选择器覆盖的。
- 看 Computed 面板,看最终计算出来的值是多少。
2. 检查优先级是否真的够高
回到上面的权重表。你是不是写了个 div,结果有个 #app .container div 把你的样式盖了?
- 解法:提高你的选择器权重,或者确保你的 CSS 写在框架 CSS 之后。
3. 层叠顺序:源顺序(Source Order)
CSS 是从上到下读取的。如果两个选择器优先级完全一样,后面写的赢。
/* 文件1.css */
.btn { color: red; }
/* 文件2.css,后引入,所以生效 */
.btn { color: blue; }
解法:调整 CSS 文件的引入顺序,或者提高你代码的优先级。
4. 作用域隔离:BEM 或 CSS Modules
现代前端项目(Vue/React)常用 CSS Modules 或 Scoped CSS。
- Vue:
<style scoped> - React: CSS Modules (
.module.css) - 现象:你写了样式,但在 HTML 里元素没有
class="xxx",而是class="xxx_123abc"。 - 解法:检查你的选择器是否匹配了生成的动态 class 名,或者使用更具体的选择器。
5. 继承 vs 层叠的误区
.parent { color: red; }
.child { color: inherit; } /* 显式继承 */
如果你写了 inherit,但父元素颜色被别的规则覆盖了,子元素也会跟着变。
解法:不用 inherit,直接让子元素自己设颜色,避免被父元素牵连。
6. 特殊性为 0 的情况
* { margin: 0; padding: 0; }
通配符权重为 0,但因为它写在最前面且覆盖了所有元素,所以生效。如果你后面写了 div { margin: 10px; }(权重 1),就能覆盖它。
六、 如何避免层叠样式覆盖?(最佳实践)
与其事后排雷,不如事前预防。以下是我作为专家的建议:
1. 使用 BEM 命名规范
BEM(Block Element Modifier)能极大降低选择器复杂性,避免权重爆炸。
/* 糟糕的写法,权重高,易冲突 */
.nav .menu .item .link { ... }
/* BEM 写法,语义清晰,权重低 */
.nav { ... }
.nav__item { ... }
.nav__item--active { ... }
BEM 的类名层级扁平,不容易出现“选择器嵌套过深导致优先级虚高”的问题。
2. 避免 ID 选择器用于样式
ID 权重 100,太高了,容易在大型项目中造成“优先级陷阱”。尽量用 class。
- 错误:
#submit-btn { ... } - 正确:
.btn-submit { ... }
3. 使用 CSS 预处理器(SCSS/Less)时注意嵌套
SCSS 的嵌套容易让选择器变得很长,无意中提高了权重。
// 容易写出高权重
.card {
.title {
.icon {
color: red; // 权重 1+1+10+10 = 22,虽然不高,但冗长难维护
}
}
}
建议:嵌套不超过 3 层,并使用 & 符号精确控制选择器结构,避免生成过长的后代选择器。
4. 使用 CSS 自定义属性(Variables)集中管理
把易变的样式放变量里,减少直接覆盖的需求。
:root {
--primary-color: #3498db;
}
.btn {
color: var(--primary-color);
}
这样你想改主题色,只改 :root 一处,不用去改各个组件的样式,从根本上减少冲突。
5. 使用 PostCSS 或 Stylelint 工具检查
配置 stylelint 规则,禁止使用 !important,禁止过深嵌套,强制使用 BEM 命名。从工程角度杜绝“脏代码”的产生。
总结
CSS 的继承和层叠不是玄学,是一套严谨的数学规则。
- 继承是血缘传递,层叠是规则竞争。
- 优先级决定胜负:内联 > ID > 类 > 元素。
- 后写的在优先级相同时胜出。
!important是例外,慎用。- 排雷首选 F12 看 Styles 面板,看哪个规则被划掉。
记住,写 CSS 不是为了“让浏览器听话”,而是为了让未来维护代码的人(包括你)能一眼看懂。清晰的命名、合理的权重、避免继承陷阱,才是写出可维护 CSS 的正道。
