游览器工作原理:CSS的渲染

CSS样式规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<html>
<head>
<style>
div {
positon: absolute;
top: 200px;
left: 200px;
width: 200px;
height: 200px;
border: 2px solod black;
padding:20px;
background-color: blue;
color: green;
opacity: 0.8;
}
</style>
</head>
<body>
<div id="simple"></div>
</body>
</html>

上边是一个使用CSS的简单例子,所有的CSS都是按照以下规则书写的: [选择器] { [样式名]: [样式值], [样式名]: [样式值], …}

位置计算

CSS的位置计算包含了两大部分,一部分是众所周知的 盒子模型,另外一部分是比较小众的 包含块模型。对于盒子模型,因为比较简单,本篇博文略过,重点讲 包含块模型。 当游览器计算元素的盒子的位置和大小时,webkit需要计算该元素和另外一个矩形区域的相对位置,这个矩形区域称为该元素的包含块。盒模型就是在包含块内计算和确定各个元素的。

  • 根元素的包含块称为初试包含块,大小就是可视区域。
  • 对于static和relative布局的元素,包含块是最佳父级元素盒子模型的content区域。
  • 对于fixed布局的元素,包含块脱离html流,固定值整个游览器可视区域的一个位置。
  • 对于absolute布局的元素,包含块由最近含有absolute、relative、fixed的祖先决定:如果一个元素具有inline属性,元素的包含块是包含该祖先的第一个和最后一个内联元素的内边距的区域;否则,包含块是该祖先的内边距的区域。

CSSOM:CSS对象模型

可能所有人都知道HTML对应的文档对象模型的DOM,但没多少听过CSS对应CSS对象模型 CSSOM 。实际当游览器要解析外联或者内联的CSS时,为了可以解析这种文档,使之可以用于样式排布和绘制。这种数据结构就是 CSSOM。它的思想是在DOM的一些节点接口中,加入获取和操作CSS属性或者接口的javascript接口,因而实现让javascript可以动态操作CSS样式。 对于样式表,CSSOM提供了一个接口,这个接口在webkit定义是:

1
2
3
4
5
6
7
8
partial interface Document {
readonly attribute StyleSheetList styleSheetLists;
attribute DOMString ? sekectedStyleSheetSet;
readonly attribute DOMString ? lastStyleSheetSet;
readonly attribute DOMString ? preferredStyleSheetSet;
readonly attribute DOMString ? StyleSheeSetsList styleSheetLists;
void enableStyleSheetsForSet(DOMString ? name);
}

通过这些属性,可以动态选择使用哪些样式表。 这个接口可以获取样式表的各种信息,例如css的’href’、css类型’type’、css规则’cssRules’。使用方法是document.stylesheets,可以得到如下结果: CSSOM

CSS解释器与匹配规则

CSS的解释分为三个过程 词法分析: 在上一章节第一节中,简单说了样式规则,在webkit内部, 样式类型有以下几个Class:

  • Style:基本类型,绝大多数属于这个类型
  • Import:引入CSS用
  • Media:对应 @media 类型
  • Fontface:CSS3中自定义字体的类型
  • Keyframes:对应css3中 @keyframes 类型
  • Page:对应 @page 类型

这些样式和值对应的 CSSValue 这个类组成了一个样式规则——被叫做CSSProperly,同一个选择器 CSSSelector 多个样式规则一起组成了 CSSPropertySet。于是形成了如下结构: 这些结构的每个节点就是 token 语法分析:由 CSSParser 类完成,但这个类是一个代理模式,实际完成的是 CSSGrammer 类完成,这个类可以解析startSelector, endSelector, startRuleBody, startProperty, parseValue, endRuleBody。 结果这两步,就生成样式规则StyleRule,会被放入 StyleSheetContents 对象中。

CSS布局计算

布局计算是一个递归的过程,每一个节点的大小都要计算其子女的位置和大小。布局计算依靠内部类 RenderObject 进行计算。整个过程如下: 对于以下几种情况会触发重新布局:

  • 当网页首次被打开的时候,游览器设置网页的可视区域,并调用计算布局的方法;
  • 网页的动画会触发布局计算,因为动画可能改变样式属性;
  • JavaScript代码通过CSSOM等直接修改样式信息;
  • 用户的交互,例如翻滚网页。 重新布局就是所谓的回流,因为不能依靠修改单纯内存中的CSSValues然后调用渲染器实现修改,必须经过布局计算全部计算并渲染,因此非常消耗时间,也是前端老生常谈的优化项之一。

游览器在进行完布局计算后,还会进行布局测试,布局测试是对游览器最重要的测试,它会测试整个网页的渲染结果,包括网页的加载和渲染两个过程。方法是预先准备大量运用测试渲染结果的单元测试用例,然后把得到的结果和已有的用例进行比对检测准确性。