今天我们来讲一个非常非常重要的知识点------CSS 的属性值计算过程。
我经常说,CSS 里有两个核心知识点:
- 属性值的计算过程
- 视觉格式化模型
如果这两个没掌握,CSS 就没算真正学会。其它那些早期的、零碎的属性只是加深理解的练习,但核心就在这两个。
一、什么是属性值的计算过程
我们平时写网页、调试样式的时候,会在浏览器的「开发者工具」里看到很多属性值,有些是我们写的,有些是默认就存在的。 这时候你可能会想:我明明没写这么多属性,为什么调试结果里都有值?
这就是属性值计算过程的核心所在。
每一个 CSS 元素,不管你写没写它的样式,最终都必须有每一个属性的值。 不可能有属性"缺失"。 如果你没写,浏览器会帮你"算"出来。 而「属性值的计算过程」就是在研究------它到底是怎么一步步算出这些最终结果的。
理解这个过程非常关键。调样式的时候,如果某个样式不生效,或者显示结果和预期不一样,你就可以根据这个过程去反推问题出在哪里。
二、计算结果(Computed Value)
我们在开发者工具里看到的最终结果,就是计算结果(Computed Value)。 真正决定页面上元素最终长什么样的,不是你写的样式,而是计算后的结果。
举个例子:
css
div {
display: inline;
float: left;
}
你写的 display
是 inline
,但最终计算结果里会变成 block
。 为什么?因为 float
与 display: inline
是冲突的,浏览器会根据规则自动转换。
所以记住:写出来的不一定是最终值,计算结果才是决定因素。
三、属性值的计算四个阶段
属性值的计算大致分为四个步骤:
- 确定声明值(Specified Value)
- 层叠(Cascade)
- 继承(Inheritance)
- 默认值(Initial Value)
下面我们逐步讲解。
1. 确定声明值
在这一步,浏览器会去查找所有和该元素相关的样式来源,包括:
- 用户编写的样式表(作者样式)
- 浏览器默认样式表(用户代理样式)
- 甚至用户自定义样式(很少用)
如果没有冲突(即同一个属性没有被多次声明),那么浏览器直接采用这个值。
同时,在这个阶段还会进行一些值的转换:
- 比如颜色
red
会被转换成rgb(255, 0, 0)
- 相对单位会被换算成绝对值
- 某些语义值会被规则修正,比如
float:left
会让display
变成block
2. 层叠(Cascade)
层叠阶段就是解决冲突。 如果同一个属性被多个地方声明了,就要判断谁"赢"。
这一步分为三个子步骤:
(1)比较重要性(Importance)
从高到低的优先级为:
- 行内样式(内联样式)
- 作者样式表(自己写的)
- 用户代理样式表(浏览器默认)
!important
最高优先级(能打破以上规则)
比如:
css
h1 {
font-size: 32px;
}
默认样式中 h1
的字体大小是 32px
, 但你在样式表中写了 font-size: 50px;
,那最终就是 50px。 因为你的样式表比浏览器默认样式表更重要。
如果默认样式里带了 !important
,那它就可能覆盖你的设置。
(2)比较特殊性(Specificity)
这就是我们常说的"优先级"、"权重"。
浏览器会为每一个选择器生成 4 个数字(通常写成 a,b,c,d):
a
:是否是内联样式(写在 style="" 里)b
:ID 选择器的数量c
:类、伪类、属性选择器的数量d
:标签、伪元素选择器的数量
举例:
css
#app .title span { color: red; }
计算权重:
类型 | 数量 | 对应数值 |
---|---|---|
内联样式 | 0 | a=0 |
ID | 1 | b=1 |
类、伪类、属性 | 1 | c=1 |
标签、伪元素 | 1 | d=1 |
最终权重:0,1,1,1
比较规则: 从左到右依次比较,高位相同才比较低位。 所以,0,1,0,0
比 0,0,99,99
更高。
(3)比较来源顺序(Source Order)
如果上面两步都一样,那就看谁写得更靠后,后面的会覆盖前面的。
3. 继承(Inheritance)
层叠之后,有的属性还是没值,比如 color
、font-size
等文字类属性。 这时候浏览器会看它的父元素有没有设置该属性。
如果父元素有且该属性是"可继承的",那就继承下来。
比如:
html
<div style="color: red;">
<p>文字</p>
</div>
虽然 p
标签没写 color
,但它的文字也是红色,因为继承了父元素的 color
。
注意:并不是所有属性都能继承,比如 margin
、border
就不行。
4. 使用默认值(Initial Value)
如果经过前面三步,属性仍然没有值,那浏览器就会使用它的默认值。
每个属性都有默认值(可以查 MDN)。 这样一来,每个元素的每个属性都保证有一个确定的值。
四、总结:属性值的完整计算过程
一个元素的每个属性,从"没有值"到"有值",完整经历了以下步骤:
- 确定声明值:找出样式表中的初始声明并进行基础转换。
- 层叠(Cascade):解决冲突,选出最终保留的声明。
- 继承(Inheritance):没有值的属性尝试继承父元素。
- 默认值(Initial Value):仍没有值的属性使用默认值。
最终得到的,就是浏览器用于渲染的「计算结果(Computed Style)」。
五、应用:如何调试样式问题
当你的样式"没生效"时:
-
打开浏览器开发者工具;
-
查看元素的「Computed」面板;
-
对比结果和预期;
-
如果不符,就按计算流程去反推:
- 有没有被覆盖?
- 有无更高权重的选择器?
- 是否可继承?
- 默认值是什么?
理解了计算过程,CSS 调试就会非常清晰。
六、延伸:它在浏览器中的位置
整个浏览器的渲染流水线(Render Pipeline)大致分为 7 个阶段:
- 样式计算(Style Calculation)
- 布局(Layout)
- 绘制(Paint)
- 分层(Layering)
- 合成(Compositing)
- 光栅化(Rasterization)
- 显示(Display)
属性值的计算过程,属于第一个阶段的核心内容。
✅ 一句话总结:
CSS 的属性值计算过程,就是浏览器确定每个元素每个属性"最终值"的完整过程。 掌握它,你就能真正读懂浏览器是怎么渲染网页的,也能彻底搞清楚为什么某个样式"不生效"。