当前位置: 首页 > Web前端 > CSS

CSS渲染原理与优化策略

时间:2023-03-30 16:29:54 CSS

推荐理由:文章由浅入深,受益匪浅。说到CSS,很多童鞋都是很不屑的,尤其是看到RedMonk2019编程语言排行榜的时候,CSS竟然排在了第七位。先来看看这份榜单:既然CSS如此重要,值得我们花点时间研究一下相关原理。本节我们会讲CSS渲染优化相关内容,主要围绕以下几点,由浅入深,了解来龙去脉:浏览器构成渲染引擎CSS特性CSS语法解析过程CSS选择器执行orderEfficientComputedStyleCSS写顺序对对性能有影响吗?优化策略Browser构成UserInterface:用户界面,包括浏览器可见的地址输入框、浏览器前进和返回按钮、书签、历史记录等用户可操作的功能选项。浏览器引擎:浏览器引擎,可以在用户界面和渲染引擎之间传递指令或者读写客户端本地缓存中的数据,是浏览器各部分之间通信的核心。Renderingengine:渲染引擎??,解析DOM文档和CSS规则,将内容排版成有样式的界面显示在浏览器中,即排版引擎。我们常说的浏览器内核主要是指渲染引擎。Networking:网络功能模块是浏览器开启网络线程发送请求和下载资源的模块。JavaScriptInterpreter:JS引擎,解释执行JS脚本部分,如V8引擎。UIBackend:UIbackend用于绘制浏览器窗口中的基本控件,如组合选择框、按钮、输入框等DataPersistence:数据持久化存储,涉及到一些客户端存储技术,如Cookie和LocalStorage,可以通过浏览器引擎提供的API调用。渲染引擎渲染引擎解析DOM文档和CSS规则,将内容排版成有样式的界面显示在浏览器中,即排版引擎。我们常说的浏览器内核主要是指渲染引擎。上图中,我们需要注意两条主线:一是HTMLParser生成的DOM树;二是CSSParser生成的StyleRules(CSSOM树);之后DOM树和StyleRules会生成一个新的对象,也就是我们常说的RenderTree,结合Layout绘制在屏幕上,这样就可以显示了。CSS特性1.优先选择器权重!important1/0(无限)内联样式1000ID100类/伪类/属性10元素/伪元素1通配符/子选择器/相邻选择器0!important>内联样式(权重1000)>ID选择器(权重100)>类选择器(权重10)>标签(权重1)>通配符>继承>浏览器默认属性示例代码一:

Jartto的博客

猜猜文本会显示什么颜色?当你知道“ID选择器>类选择器”时,答案不言而喻。升级它:Jartto的博客

添加了!important,猜猜文本显示的颜色是什么?3.CascadingCascading是浏览器将多个样式源进行叠加,最终确定结果的过程。CSS有“级联”的概念,因为有多种样式来源。CSS级联是指当一个CSS样式为同一个元素配置相同的属性时,根据级联规则(权重)处理冲突,选择并应用CSS选择器指定的权重高的属性,一般描述为由于高权重覆盖了低权重,所以也叫stacking。示例代码三:
Jartto的博客

权重:002101010101验证:Jartto的博客
weight:002101010011如果你熟悉以上问题,恭喜你,基础部分已经通过测试,可以继续升级了!CSS语法解析过程1、我们先来看一下CSS。HTMLParser会生成一个DOM树,CSSParser会将解析结果附在DOM树上,如下图:2.CSS有自己的规则,大致如下:WebKit使用CSS语法文件自动创建解析器Flex和Bison解析器生成器。Bison创建了一个自下而上的shift-reduce解析器。Firefox使用人工编写的自上而下的解析器。两个解析器都将CSS文件解析为StyleSheet对象,每个对象都包含CSS规则。CSS规则对象包含选择器和声明对象,以及与CSS语法对应的其他对象。3.CSS解析过程会根据Rule和Declaration进行操作:4.那么它是如何解析的呢,我们不妨打印CSSRules:consoleinput:document.styleSheets[0].cssRules打印出来的结果大致分为几种Class:cssText:存放当前节点规则字符串parentRule:父节点的规则parentStyleSheet:包含cssRules,ownerNode,rules规则...规则好像有点看不懂,不着急,我们往下看。5、CSS解析和Webkit有什么关系?CSS依赖于WebCore进行解析,而WebCore是Webkit非常重要的一个模块。要了解WebCore是如何解析的,我们需要查看相关源码:CSSRule*CSSParser::createStyleRule(CSSSelector*selector){CSSStyleRule*rule=0;如果(选择器){rule=newCSSStyleRule(styleElement);m_parsedStyleObjects.append(规则);规则->setSelector(sinkFloatingSelector(selector));rule->setDeclaration(newCSSMutableStyleDeclaration(rule,parsedProperties,numParsedProperties));}清除属性();退货规则;当解析器满足某种条件,需要创建一个CSSStyleRule时,就会调用这个函数。该函数的作用是创建一个CSSStyleRule,并将其添加到解析后的样式对象列表m_parsedStyleObjects中,其中该对象指的是Rule。注:源码仅供参考和理解,无需逐行阅读!Webkit使用自动代码生成工具来生成相应的代码,也就是说自动生成词法分析和语法分析的代码,Webkit中实现的CallBack函数在CSSParser中。这个时候就不得不提到AST了,我们继续分析。补充阅读:Webkit支持CSS6关于AST如果你还不了解AST,请移步AST抽象语法树。我们这里不做过多解释,主要关注如何解析这个过程。看一个Babel转换过程图:举个简单的例子,声明一个箭头函数,如下:letjarttoTest=()=>{//Todo}通过在线编译,生成如下结果:由上面如图,我们可以看到我们的箭头函数被解析成了一段标准的代码,包括类型、开始位置、结束位置、变量声明类型、变量名、函数名、箭头函数表达式等等。标准的解析代码,我们可以对其进行加工处理,然后通过相应的API输出。这个过程用在很多场景,比如:JS反编译,语法分析。Babel编译ES6语法。代码高亮。关键字匹配。范围判断。代码压缩。...场景千千万万,但都离不开一个过程,那就是:AST转换过程:解析-转换-生成这里,我们很清楚CSS是如何解析的来龙去脉,我们可以回到文章开头的流程图,相信你会有另一种感悟。CSS选择器执行顺序渲染引擎从右到左解析CSS选择器。为什么?例如:

111

222

333

444

我们以“从左到右”的方式分析:先找到所有的div节点。查找带有class="jartto"的div节点内的所有子div。然后依次匹配pspan.yellow等。如果不匹配,则必须回到最开始查找的div或p节点,再查找下一个节点,重复这个过程。这样的搜索过程对于只匹配少数几个节点的选择器来说是极其低效的,因为我们花了很多时间回溯来匹配不符合规则的节点。我们以“从右到左”的方式分析:首先找到class="yellow"的span元素。然后检查父节点是否为p元素,如果不是,则进入同级其他节点的遍历,如果是,则继续匹配父节点满足class="jartto"的div容器。这样,集合的元素又减少了,只有当前的子规则会匹配上一个子规则。综上所述,我们可以得出结论,浏览器CSS匹配核心算法规则是从右到左匹配节点。这样做是为了减少无效匹配的数量,使匹配更快,性能更好。所以我们写CSSSelector的时候,从右到左匹配节点的SelectorTerm越少越好。不同的CSS解析器对CSSRules的解析速度有很大差异。有兴趣的童鞋可以看看CSS解析引擎,这里就不赘述了。高效的ComputedStyle浏览器也有一个很好的策略。在某些情况下,浏览器会共享ComputedStyle。网页中有很多可以共享的标签,可以大大提高执行效率!如果可以共享,那么就不需要执行匹配算法了,执行效率自然很高。如果两个或多个Element的ComputedStyle通过计算没有确认相等,那么这些ComputedStyle相等的Element的ComputedStyle只会计算一次样式,其余的只会共享ComputedStyle。一个

两个

如何高效分享ComputedStyle?TagName和Class属性必须相同。不能有Style属性。即使Style属性相等,它们也不会共享。3.不能使用兄弟选择器,如:first-child、:last-selector、+选择器。4.mappedAttribute必须相等。为了更好的说明,我们再举两个例子:不分享,上面规则2:jartto's

blog

可以是分享,上面的规则4:jartto's

blog

到这里,相信大家对ComputedStyle有了更进一步的了解,代码也会更精致高效。CSS书写顺序会影响性能吗?需要注意的是,浏览器并不是一拿到CSS样式就立即开始解析,而是按照CSS样式的书写顺序,按照DOM树的结构分布渲染样式,然后开始遍历每个树节点的CSS样式。解析,此时CSS样式的遍历顺序完全按照之前的书写顺序。在解析过程中,一旦浏览器发现某个元素的定位变化影响了布局,就需要返回去重新渲染。让我们看一下下面的代码片段:width:150px;高度:150px;字体大小:24px;位置:绝对;但是,它是按照普通元素解析的,所以要重新渲染。渲染引擎首先释放该元素在文档中的位置,这会导致该元素的位置发生变化,其他元素可能会因其回流而重新排序。我们调整代码:position:absolute;宽度:150px;高度:150px;字体大小:24px;这样会让渲染引擎的工作效率更高,但是问题来了:在实际的开发过程中,我们如何保证自己的写法顺序是最好的呢?这里有个规范,建议顺序大致如下:定位属性positiondisplayfloatlefttoprightbottomoverflowclearz-index自己的属性widthheightpaddingbordermarginbackgroundtextstylefont-familyfont-sizefont-stylefont-weightfont-变色文本属性text-alignvertical-aligntext-wraptext-transformtext-indenttext-decorationletter-spacingword-spacingwhite-spacetext-overflowCSS3内容中的新属性box-shadowborder-radiustransform当然,我们需要知道这个规则就够了,剩下的可以交给一些插件,比如CSLint(可以用代码实现,不要浪费人力)。优化策略从浏览器组成,到渲染引擎,再到CSS解析原理,最后到执行顺序,我们做了一系列的探索。希望大家能从CSS的渲染原理上理解整个过程,从而写出更高效的代码。1.使用id选择器效率很高。使用id选择器时需要注意一点:因为id是唯一的,所以不需要同时指定id和tagName:/*Bad*/p#id1{color:red;}/*Good*/#id1{color:red;}2.避免深度节点,例如:/*Bad*/div>div>div>p{color:red;}/*Good*/p-class{color:red;}3。不要使用属性选择器如:p[att1="val1"],这样的匹配很慢。不要这样写:p[id="id1"],它会将id选择器退化为属性选择器。/*差*/p[id="jartto"]{color:red;}p[class="blog"]{color:red;}/*好*/#jartto{color:red;}.blog{color:red;}4.将浏览器前缀放在前面,将标准样式属性放在最后,例如:.foo{-moz-border-radius:5px;border-radius:5px;}可以参考这个css规范。5.遵守CSSLint规则font-faces       不能使用超过5种网页字体在属性选择器中-selector    禁止使用通用选择器*unqualified-attributes   禁止使用非标准属性选择器零单位     0overqualified后不要添加单位-elements   使用relative相邻选择器时,不要使用不必要的选择器shorthand        shorthand样式属性duplicate-background-images相同的url在样式表中不要多次使用6.减少CSS文档大小去除空CSS规则(删除空规则)。值0不需要单位。使用缩写。属性值为浮点数0.xx,小数点前的0可以省略。不要为h1-h6元素定义太多样式。7.CSSWillChangeWillChange属性可以让作者提前通知浏览器默认样式,使用一个专门的属性通知浏览器注意下一次变化,从而优化和分配内存。8、不要使用@import使用@import引入CSS会影响浏览器的并行下载。只有@import引用的CSS文件下载解析完成后,浏览器才会知道还有一个CSS需要下载,然后下载,下载完成后开始解析和构建RenderTree等一系列操作。多个@imports将导致乱序下载。在IE中,@import会导致资源文件的下载顺序被打乱,即排在@import之后的JS文件会在@import之前被下载,并且会打乱甚至破坏@import本身的并行下载。9.避免过多的回流/回流(Reflow)浏览器重新计算布局位置和大小。常见重排元素:widthheightpaddingmargindisplayborder-widthbordertoppositionfont-sizefloattext-alignoverflow-yfont-weightoverflowleftfont-familyline-heightvertical-alignrightclearwhite-spacebottommin-height10。有效使用computedStyle公共类。谨慎使用ChildSelector。尽可能分享。有关更多信息,请参见上文-EfficientComputedStyle11。减少昂贵的属性当页面被重绘时,它们会降低浏览器的渲染性能。所以在写CSS的时候,我们应该尽量减少使用昂贵的属性,比如:box-shadow。边界半径。筛选。:第n个孩子。12.依赖继承如果有些属性是可以继承的,那自然就不用再写了。13.遵守CSS顺序规则以上是对本文的总结。了解CSS的具体实现原理,知道如何避免写错,知道为什么这么优化就够了。性能优化是无止境的。文章首发于Jartto的博客