从Chrome源码看浏览器如何计算CSS - 前端加油站 - 科蚁网
请选择 进入手机版 | 继续访问电脑版

从Chrome源码看浏览器如何计算CSS

在《Effective前端6:避免页面卡顿》这篇里面介绍了浏览器渲染页面的过程:

并且《从Chrome源码看浏览器如何构建DOM树》介绍了第一步如何解析Html构建DOM树,这个过程大概如下:

浏览器每收到一段html的文本之后,就会把它序列化成一个个的tokens,依次遍历这些token,实例化成对应的html结点并插入到DOM树里面。

我将在这一篇介绍第二步Style的过程,即CSS的处理。

1. 加载CSS

在构建DOM的过程中,如果遇到link的标签,当把它插到DOM里面之后,就会触发资源加载——根据href指明的链接:

上面的rel指明了它是一个样式文件。这个加载是异步,不会影响DOM树的构建,只是说在CSS没处理好之前,构建好的DOM并不会显示出来。用以下的html和css做试验:

demo.css如下:

从打印的log可以看出(添加打印的源码略):

[DocumentLoader.cpp(558)] “\n\n\n \n\n\n

\n   

hello, world

\n
\n\n\n”

[HTMLDocumentParser.cpp(765)] “tagName: html  |type: DOCTYPE|attr:              |text: “

[HTMLDocumentParser.cpp(765)] “tagName:            |type: Character |attr:              |text: \n”

[HTMLDocumentParser.cpp(765)] “tagName: html  |type: startTag    |attr:              |text: “

[HTMLDocumentParser.cpp(765)] “tagName: html  |type: EndTag     |attr:              |text: “

[HTMLDocumentParser.cpp(765)] “tagName:            |type: EndOfFile|attr:              |text: “

[Document.cpp(1231)] readystatechange to Interactive

[CSSParserImpl.cpp(217)] recieved and parsing stylesheet: “.text{\n   font-size: 20px;\n}\n.text p{\n    color: #505050;\n}\n”

在CSS没有加载好之前,DOM树已经构建好了。为什么DOM构建好了不把html放出来,因为没有样式的html直接放出来,给人看到的页面将会是乱的。所以CSS不能太大,页面一打开将会停留较长时间的白屏,所以把图片/字体等转成base64放到CSS里面是一种不太推荐的做法。

2. 解析CSS

(1)字符串 -> tokens

CSS解析和html解析有比较像的地方,都是先格式化成tokens。CSS token定义了很多种类型,如下的CSS会被拆成这么多个token:

经常看到有人建议CSS的色值使用16位的数字会优于使用rgb的表示,这个是子虚乌有,还是有根据的呢?

如下所示:

如果改成rgb,它将变成一个函数类型的token,这个函数需要再计算一下。从这里看的话,使用16位色值确实比使用rgb好。

(2)tokens -> styleRule

这里不关心它是怎么把tokens转化成style的规则的,我们只要看格式化后的styleRule是怎么样的就可以。每个styleRule主要包含两个部分,一个是选择器selectors,第二个是属性集properties。用以下CSS:

打印出来的选择器结果为(相关打印代码省略):

selector  text    = “.text .hello”

                value = “hello”  matchType = “Class” relation = “Descendant”

                tag history selector text = “.text”

                value = “text”    matchType = “Class” relation = “SubSelector”

selector  text    = “#world”

                value = “world” matchType = “Id”      relation = “SubSelector”

从第一个选择器可以看出,它的解析是从右往左的,这个在判断match的时候比较有用。

blink定义了几种matchType:

还定义了几种选择器的类型:

.text .hello的.hello选择器的类型就是Descendant,即后代选择器。记录选择器类型的作用是协助判断当前元素是否match这个选择器。例如,由于.hello是一个父代选器,所以它从右往左的下一个选择器就是它的父选择器,于是判断当前元素的所有父元素是否匹配.text这个选择器。

第二个部分——属性打印出来是这样的:

selector text = “.text .hello”

    perperty id = 15 value = “rgb(200, 200, 200)”

    perperty id = 316 value = “calc(100% – 20px)”

selector text = “#world”

    perperty id = 147 value = “20px”

    perperty id = 146 value = “20px”

    perperty id = 144 value = “20px”

    perperty id = 145 value = “20px”

所有的CSS的属性都是用id标志的,上面的id依次对应:

设置了margin: 20px,会转化成四个属性。从这里可以看出CSS提倡属性合并,但是最后还是会被拆成各个小属性。所以属性合并最大的作用应该在于减少CSS的代码量。

 


鲜花
1

握手

雷人

路过

鸡蛋

刚表态过的朋友 (1 人)

该文章已有0人参与评论

请发表评论

全部评论