CSS引擎分析.docx

上传人:b****0 文档编号:12472608 上传时间:2023-04-19 格式:DOCX 页数:15 大小:113.09KB
下载 相关 举报
CSS引擎分析.docx_第1页
第1页 / 共15页
CSS引擎分析.docx_第2页
第2页 / 共15页
CSS引擎分析.docx_第3页
第3页 / 共15页
CSS引擎分析.docx_第4页
第4页 / 共15页
CSS引擎分析.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

CSS引擎分析.docx

《CSS引擎分析.docx》由会员分享,可在线阅读,更多相关《CSS引擎分析.docx(15页珍藏版)》请在冰豆网上搜索。

CSS引擎分析.docx

CSS引擎分析

WebkitCSS引擎分析

分类:

webkit2011-12-1010:

03161人阅读评论(0)收藏举报

目录(?

)[+]

转载自

WebkitCSS引擎分析

浏览器CSS模块负责CSS脚本解析,并为每个element计算出样式。

CSS模块虽小,计算量大,设计不好往往成为浏览器性能的瓶颈。

CSS模块在实现上有几个特点:

CSS对象众多(颗粒小而多),计算频繁(为每个element计算样式)。

这些特性决定了webkit在实现CSS引擎上采取的设计,算法。

如何高效的计算样式是浏览器内核的重点也是难点。

1前端工程师可能更关注:

2能被浏览器高效执行的CSS脚本

3浏览器内核工程师可能更关注:

4CSS内部数据的组织

5计算样式

6思考

7总结

高效执行的CSS脚本

我这里仅从webkit执行的性能上来讲高效的CSS,不涉及CSS设计问题。

如果两个或多个element的computedStyle不通过计算可以确认他们相等,那么这些computedStyle相等的elements只会计算一次样式,其余的仅仅共享该computedStyle。

例如:

viewplaincopytoclipboard

7

7

7CellOne

7

7

7CellTwo

7

[html]viewplaincopy

7

7

7CellOne

7

7

7CellTwo

7

8

那么两个tr共享computedStyle,两个td共享computedStyle。

在内核里,只会计算第一个tr和第一个td的ComputedStyle。

那么如何做到共享computedStyle呢:

该共享的element不能有id属性且CSS中还有该id的StyleRule.哪怕该StyleRule与Element不匹配。

譬如:

viewplaincopytoclipboard

8div#id1{color:

red}

8

8

paragraph1

8paragraph2

[html]viewplaincopy

8div#id1{color:

red}

8

8

paragraph1

8paragraph2

8

可以看到这两个p标签computedStyle本来是一样的,但他们不共享。

8tagName和class属性必须一样。

8mappedAttribute必须相等。

不能有style属性。

哪怕style属性相等,他们也不共享。

例如:

viewplaincopytoclipboard

8

red">paragraph1

8

red">paragraph2

8

[html]viewplaincopy

8

red">paragraph1

8

red">paragraph2

8

8

他们并不共享computedStyle。

8不能使用siblingselector,譬如:

first-child,:

last-selector,+selector.

使用idselector非常的高效。

在使用idselector的时候需要注意一点:

因为id是唯一的,所以不需要既指定id又指定tagName。

例如:

viewplaincopytoclipboard

8Bad

8p#id1{color:

red;}

8Good

8#id1{color:

red;}

[html]viewplaincopy

8Bad

8p#id1{color:

red;}

8Good

8#id1{color:

red;}

9

使用classselector的策略与idselector一样。

在内核实现上,idselector与classselector的匹配并没有多大的区别。

如果同一个class需要赋予不同的css,你可以这样做

viewplaincopytoclipboard

9Bad

9p.class1{color:

red;}

9div.class1{color:

black;}

9Good

9p-class1{color:

red;}

9div-class1{color:

black;}

[html]viewplaincopy

9Bad

9p.class1{color:

red;}

9div.class1{color:

black;}

9Good

9p-class1{color:

red;}

9div-class1{color:

black;}

当然这样会造成网页中的className增多。

具体您决定怎么取舍。

有时要选择的node比较深时,我们可以采取如下写法:

viewplaincopytoclipboard

9Bad

9div>div>div>p{color:

red;}

9Good

9p-class{color:

red;}

[html]viewplaincopy

9Bad

9div>div>div>p{color:

red;}

9Good

9p-class{color:

red;}

10

ChildSelector的匹配比较的慢。

不到万不得已,不要使用attributeselector。

例如:

p[att1="val1"]。

这样的匹配非常慢。

更不要这样写:

p[id="id1"]。

这样将idselector退化成attributeselector。

viewplaincopytoclipboard

10Bad

10p[id="id1"]{color:

red;}

10p[class="class1"]{color:

red;}

10Good

10#id1{color:

red;}

10.class1{color:

red;}

[html]viewplaincopy

10Bad

10p[id="id1"]{color:

red;}

10p[class="class1"]{color:

red;}

10Good

10#id1{color:

red;}

10.class1{color:

red;}

11

12依赖继承。

如果某些属性可以继承,那么自然没有必要在写一遍。

13其他的selector在内核实现上很难做出优化,所以如果可以的话尽量不用。

WebkitCSS模块实现

这里我更多的希望分享我实际开发CSS中所碰到的一些问题,透过这些问题来看webkit的设计也许更有体会。

一些名词的解释

有些webkit内核使用的名词这里作下解释,如果对这些名词不理解,那么对研究代码有一定的阻力

∙mappedAttribute:

一些可以影响CSSComputedStyle的html属性。

举个例子:

paragraph

那么属性align="middle"就叫做mappedAttribute。

一般大家都知道每个Element有个inlineStyleDeclaration,实际上还有个隐含的Declaration叫MappedStyleDeclaration.他的优先级比普通的CSS高,比inlineStyle要低。

∙renderStyle:

这就是大家熟悉的ComputedStyle在webkit中的表示。

∙bloomfilter:

一种算法。

没接触过的可以网上搜索。

CSS内部数据的组织

这里不想画一些css对象的继承图。

对象继承图可以参考这篇文章。

并且我假设读者已经熟知CSS相关规范,概念。

解析完CSS脚本后,会生成CSSStyleSheetList,他保存在Document对象上。

为了更快的计算样式,必须对这些CSSStyleSheetList进行重新组织。

(思考,你能直接从CSSStyleSheetList上计算样式吗?

计算样式就是从CSSStyleSheetList中找出所有匹配相应元素的property-value对。

匹配会通过CSSSelector来验证,同时需要满足层叠规则。

一种简单但效率偏低的组织方式,暂且称之为数组模型。

将所有的declaration中的property组织成一个大的数组。

数组中的每一项纪录了这个property的selector,property的值,权重(层叠规则)。

例如:

viewplaincopytoclipboard

14

19

20重新组织之后的数组数据为(weight我只是表示了他们之间的相对大小,并非实际值。

):

21

22selectorpropertyweight

231,acolor:

yellow1

242,p>acolor:

red2

253,p>abackground-color:

black2

264,divmargin:

1px3

27

[html]viewplaincopy

28

33

34重新组织之后的数组数据为(weight我只是表示了他们之间的相对大小,并非实际值。

):

35

36selectorpropertyweight

371,acolor:

yellow1

382,p>acolor:

red2

393,p>abackground-color:

black2

404,divmargin:

1px3

41

可以看到每一个property成为数组的一项。

相同的tagName在数组中的位置相邻,譬如selectora和selectorp>a在数组中相邻。

所有的property以selector的tagName顺序存放。

有了这样的数组组织,你可以想想了,该如何计算样式呢?

一种高效的组织方式,暂且称之为hash模型。

webkit使用CSSRuleSet对象来组织这些数据。

CSSRuleSet是这样一个对象:

他内部有4个hash表,分别为idRules,classRules,tagNameRules,universalRules。

这些hash表的定义是这样的:

HashMap

CSSRuleDataList是一个list,其总每一项为CSSRuleData。

CSSRuleData保存了一个css的styleRule,以及这个styleRule的selector的specificity(可以理解成权值)。

在CSSRuleData的constructor中会计算selector的bloomfilter值。

下图为一个粗略的图示,我并没有完整的画出各个类的定义,但已经可以帮助我们理解这些类的关系:

∙将defaultstylesheet,userstylesheet,authorstylesheet存放在不同的CSSRuleSet上。

而数组模型会将所有的stylesheet组织到一个数组中。

不要小瞧这步动作,这已经让我眼前一亮,他关系到后面的匹配算法部分。

每个CSSRuleSet将所有的stylerule分别组织到idRules,classRules,tagNameRules,universalRules。

譬如:

viewplaincopytoclipboard

41#id1{color:

red;}-->存放在idRules中。

41.class1{color:

red;}-->存放在classRules中。

41.class1{color:

red;}-->同上。

41p{color:

red;}-->在tagNameRules中。

41*{color:

red;}-->在universalRules中。

41

[html]viewplaincopy

41#id1{color:

red;}-->存放在idRules中。

41.class1{color:

red;}-->存放在classRules中。

41.class1{color:

red;}-->同上。

41p{color:

red;}-->在tagNameRules中。

41*{color:

red;}-->在universalRules中。

41

∙内核在在当前所有的stylesheet都已经请求结束,CSSparser结束之后进行组织数据这个动作。

计算样式

样式的计算如果设计的不当,直接影响浏览器内核的性能,所以这里的算法值得大家仔细的分析。

数组模型

我们将匹配之后的结果放在一个数组中,这个数组初始size为CSSproperty的个数。

暂且称这个数组为结果数组。

将defaultstylesheet,userstylesheet,authorstylesheet组织成一个大的数组之后。

要匹配一个标签,譬如:

viewplaincopytoclipboard

41

link

41计算标签a的样式:

[html]viewplaincopy

41

link

41计算标签a的样式:

42

因为数组的tagName是顺序的,所以可以使用二分查找法,找到a的开始位置和结束位置,此时为1-->3.

43针对数组的每一项进行checkselector,如果checkselector成功,存放在结果数组中。

44将匹配的结果存放在结果数组中的时候,需要判断结果数组中是否已经有了该property,如果已经存在则需要比较这两个property的权值,如果新的property权重大于老的,那么需要替换数组中的这一项。

45对数组中所有tagName为universaltagName进行匹配。

重复2-3。

总结:

可以看到该算法需要匹配所有tagName相同的项,以及所有universaltagName。

在checkselector成功之后插入结果数组中,还需要判断是否已经存在了该property。

还有一个更严重的问题,该算法在checkselector的时候,没有保存匹配的selector的相关信息,为以后的局部更新带来了非常多的不确定性问题,导致局部排版无法判断是否需要重排。

对比webkit存在非常多的动作来将不确定的因素确定化,来优化排版所需要的动作。

hash模型

在数组模型中,计算的结果存放在一个数组中。

在hash模型中,也是将计算的结果存放在一个数组中:

Vectorm_matchedRules;

46首先判断该element是否存在可以共享的renderStyle。

是否可以共享的条件较多,这里不详述,粗略的可以参看这里。

但这个策略非常非常棒,网页中能共享的标签非常多,所以能极大的提升执行效率!

如果能共享,那就不需要执行匹配算法了,执行效率自然非常高。

我在对的测试中,17864次计算样式,有4764次共享。

将近27%的计算样式的过程不需要进行,意味着此处性能提高约27%左右!

该网站共有9686个node,3412个element。

依次匹配defaultStyleSheet,userStyleSheet,authorStyleSheet,并将结果存放在结果数组中。

并记录各种stylesheet匹配结果在结果数组中的起始位置。

46每一个StyleSheet匹配的算法:

46如果该Element有id属性,那么从CSSRuleSet的idhashtable中取出相应的CSSRuleDataList

46依次测试CSSRuleDataList中的每一项CSSRuleData。

这里首先会利用bloomfilter算法过滤掉不符合条件的CSSStyleRule。

46在checkselector过程中,如果匹配成功将其加入到结果数组中。

46根据权值对结果数组进行排序。

46如果该Element有class属性,那么从CSSRuleSet的classhashtable中取出相应的CSSRuleDataList。

重复执行步骤2-->步骤4

46根据Element的tagName,从CSSRuleSet种取出tagName对应的CSSRuleDataList,重复步骤2-->步骤4.

46对所有universaltagName的CSSRuleDataList重复步骤2-->步骤4.

上述步骤生成结果数组的算法流程图如下:

47得到了所有匹配的stylerule之后,需要根据这个结果生成renderstyle。

算法步骤如下图,请看图的时候注意两个问题:

47如何体现层叠规则中不同样式表的权重。

47如何体现同一个样式表中相同的property,相同的权重,后面的覆盖前面的。

48

一些思考题

在这一篇文档中实在很难将一个内核模块的所有问题阐述清楚,所以我这里列举一些问题供大家讨论学习。

这些问题也是我在实际工具做所碰到一些具体问题,没有实际开发过CSS模块很难体会到这些问题,而且webkit对这些问题处理的很好,给了我很多启发。

49如果style标签写在了body区域,webkit在解析完这个style标签如何做呢?

style标签写在了body区域好吗?

50:

hover伪类在CSS应用很广泛,想想浏览器内核该怎么做?

需要在一个element收到hover状态的时候,重新计算css吗?

51CSS对象粒度小但数目大,大到CSSStyleSheetList,小到一个CSSValue都要使用CSS对象来表示。

而CSS文档又比较大,这样内存会不会碎片太多?

52CSS对象粒度小但数目大,重复分配释放除了碎片大,也很花费时间,想想有什么好办法吗?

考虑自己管理CSS对象的内存?

53CSS对象粒度小但数目大,这些小粒度对象能共享吗?

设计模式里有个Flyweight模式,CSS里有很好体现。

54一个Element的class属性或者id属性变了,需要重新计算renderstyle以看是否需要重排这个Element。

我们知道有siblingselectorp.class1+a那是不是这个改变了属性的Element的所有兄弟都需要重新计算renderstyle或者说重新排版呢?

55有:

first-child伪类,那是不是意味着往父亲节点中插入一个头结点,所有的孩子都需要重排呢?

因为他们可能有:

first-childselector。

webkit怎么做呢?

56cssvalue中有个值为inherit,表示使用他的parent的属性值。

如果他的parent的属性值变了呢,孩子的值如何更新?

57这篇文章对bloomfilter算法在css中应用讲的不多,但这也确实对CSScheckselector进行了不少的优化。

有兴趣的读者可以参考以下三点去看webkit源码:

57在生成CSSRuleData对象的时候,有bloomfilter数据生成。

57在parsehtml的时候,每个element的beginParsingChildren事件中会更新bloomfilter。

57在matchRulesForList的时候,方法fastRejectSelector就是过滤发生的地方。

58注意:

该优化的动作只在新的webkit版本才有,较老的webkit版本没有此动作。

59computedStyle记录了每个Element的所有的property的值,浏览器排版引擎会非常频繁的从computedStyle中取出某个property的值,将computedStyle设计成一个数组可以吗?

webkit使用renderStyle这个对象来表示computedStyle,这个对象在设计上有什么优点?

重点应关注两点:

59这个对象比数组的形式节省了非常多的内存。

59这个对象比数组的形式节省了非常多的检索时间。

总结

CSS引擎做的事情非常少(解析和计算样式),往往被大家忽略,但要设计出灵活高效的CSS引擎确实不易。

通过剖析webkitCSS的实现,经常有些设计的亮点让我激动很久,所以不要忽略webkitcss模块,他会给你惊喜!

展开阅读全文
相关搜索

当前位置:首页 > 初中教育 > 英语

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1