当前位置: 首页 > 科技观察

浏览器渲染原理介绍

时间:2023-03-19 09:49:09 科技观察

看到这个标题,你一定会想到这篇神文《How Browsers Work》。这篇文章详细解释了浏览器的很多细节,也已经翻译成中文了。为什么我还想写一个?出于两个原因,这篇文章太长且成本太高,无法一口气读完。花了很多时间看完这篇文章,能学到很多东西,但对我的工作似乎帮助不大。因此,我打算写这篇文章来解决以上两个问题。我希望您可以在上班途中或坐在马桶上时阅读它,并从中学到一些可以应用到您的工作中的东西。浏览器的大工作流程就不废话了,先看一张图:从上图我们可以看出几件事:1)浏览器会解析三样东西:一个是HTML/SVG/XHTML,在事实上,Webkit有三个C++类分别对应这三种类型的文档。解析这三个文件会生成一个DOMTree。CSS,解析CSS会生成一个CSS规则树。Javascript,脚本,主要是通过DOMAPI和CSSOMAPI来操作DOMTree和CSSRuleTree。2)解析完成后,浏览器引擎会通过DOMTree和CSSRuleTree构建RenderingTree。注意:RenderingTree并不等同于DOM树,因为像Header或者display:none这样的东西不一定放在渲染树中。CSS的RuleTree主要是完成匹配,将CSSRule附加到RenderingTree上的每个Element上。那就是DOM节点。这就是所谓的框架。然后,计算出每个Frame(也就是每个Element)的位置,也称为layout和reflow过程。3)最后调用操作系统NativeGUI的API进行绘制。DOM解析HTML的DOM树解析如下:ThisisanexampleWebpage.<

上面的HTML将被这样解析:这是另一种带有SVG标签的情况。CSS解析CSS解析大概如下(下面主要是Gecko,这是Firefox的玩法),假设我们有如下HTML文档:AfewquotesFranklinsaidthat“Apennysavedisapenyearned。”FDR说“我们没什么可害怕的,只有害怕自己。所以DOM树看起来像这样:然后我们的CSS文档像这样:/*rule1*/doc{display:block;text-indent:1em;}/*rule2*/title{display:block;font-size:3em;}/*rule3*/para{display:block;}/*rule4*/[class="emph"]{font-style:italic;}因此我们的CSS规则树将如下所示:注意图中的第四条规则有两次,一次是独立的,一次是rule3的child。因此,我们可以知道CSSRuleTree的建立需要类比DOMTree。CSS匹配DOMTree主要是从右到左解析CSSSelector。很多人认为这样会更快,其实不一定。关键看我们的CSSSelector是怎么写的。注意:CSS匹配HTML元素是一件相当复杂且对性能至关重要的事情。所以,你会看到很多地方很多人告诉你,DOM树要小,CSS尽量用id和class,不要过渡到级联,...#p#通过这两棵树,我们可以得到一个叫做StyleContextTree,它是这样的(attachCSSRulenodetoDOMTree):所以,基本上,Firefox通过CSS解析生成CSSRuleTree,然后通过比较DOM生成StyleContextTree,然后Firefox是通过将StyleContextTree与其RenderTree(FrameTree)相关联来完成的。注意:RenderTree会移除一些不可见的节点。Firefox中所谓的Frame就是一个DOM节点,不要被它的名字搞混了。注意:与Firefox不同,Webkit使用两棵树来执行此操作。Webkit还有一个Style对象,它直接将Style对象存储在对应的DOM节点上。Rendering渲染过程基本如下(黄色四步):计算CSS样式构建RenderTreeLayout——定位坐标和大小,是否换行,各种position,overflow,z-index属性...官方绘图注意:the上面的过程中有很多连接线,说明Javascript动态修改了DOM属性或者CSS属性,会导致重新布局。有些改动不会,就是那些指向天的箭头,比如修改后的CSS规则不匹配等等。这里要讲两个重要的概念,一个是Reflow,一个是Repaint。这两个不是一回事。Repaint——部分画面需要重新绘制,比如某个CSS的背景色发生了变化。但是元素的几何尺寸并没有改变。Reflow——意味着组件的几何尺寸发生了变化,我们需要重新验证和计算RenderTree。部分或全部渲染树已更改。这就是回流,或布局。(HTML使用flowbasedlayout,即流式布局,所以如果某个元素的几何尺寸发生变化,需要重新布局,也叫reflow)reflow会从根frame递归开始,依次计算所有节点的几何尺寸和位置。在回流过程中,可能会添加一些框架。例如,文本字符串必须换行。下面是维基百科打开时的Layout/reflow视频(注:HTML在初始化的时候也会做一次reflow,称为initialreflow),大家可以感受一下:Reflow的成本比Repaint的成本高很多。DOMTree中的每个节点都有回流的方法,一个节点的回流很可能会引起子节点的回流,甚至父节点和同级节点的回流。在一些高性能的电脑上可能没问题,但是如果reflow发生在手机上,那么这个过程就很痛苦,很耗电。因此,以下操作可能会比较昂贵。当你添加、删除或修改DOM节点时,移动DOM的位置或制作动画时会导致Reflow或Repaint。当你修改CSS样式时。当你调整窗口大小时(移动端没有这个问题),或者滚动时。当您修改网页的默认字体时。注意:display:none会触发reflow,visibility:hidden只会触发repaint,因为没有发现position变化。关于滚动我再多说几句。一般来说,如果我们页面的所有像素在滚动的时候都会滚动,那么性能上是没有问题的,因为我们的显卡不适合这种全屏像素。向下移动的算法很快。但是如果你有一个固定的背景图片,或者一些元素不滚动,而一些元素是动画的,那么这个滚动动作对于浏览器来说将是一个相当痛苦的过程。您可以看到其中许多页面的滚动性能有多差。因为滚动也可能导致回流。基本上,回流有以下原因:初始。页面初始化时。增加的。当一些Javascript正在操作DOM树时。调整大小。一些其他元素的尺寸已更改。样式改变。如果CSS属性已更改。肮脏的。同一帧的子树上发生了几次增量回流。好吧,我们来看一个例子:varbstyle=document.body.style;//cachebstyle.padding="20px";//reflow,repaintbstyle.border="10pxsolidred";//reflowandrepaintbstyle.coloragain="blue";//repaintbstyle.backgroundColor="#fad";//repaintbstyle.fontSize="2em";//reflow,repaint//newDOMelement-reflow,repaintdocument.body.appendChild(document.createTextNode('dude!'));当然,我们的浏览器是聪明的,不会像上面那样,每换一次样式,就reflow或者repaint一次。一般来说,浏览器会累积一批这样的操作,然后做一次回流,也叫异步回流或增量异步回流。但在某些情况下,浏览器不会这样做,例如:调整窗口大小,更改页面的默认字体等。对于这些操作,浏览器会立即回流。#p#但有时,我们的脚本会阻止浏览器执行此操作,例如:如果我们请求以下某些DOM值:offsetTop、offsetLeft、offsetWidth、offsetHeightscrollTop/Left/Width/HeightclientTop/Left/Width/HeightIE中的getComputedStyle(),或者currentStyle,因为如果我们的程序需要这些值,浏览器需要返回最新的值,这样也会flush掉一些样式变化,导致频繁reflow/repaint。减少回流/重绘以下是一些最佳实践:1)不要一一修改DOM样式。与其这样,还不如预先定义css的class,然后修改DOM的className。//badvarleft=10,top=10;el.style.left=left+"px";el.style.top=top+"px";//Goodel.className+="theclassname";//Goodel.style.cssText+=";left:"+left+"px;top:"+top+"px;";2)离线修改DOM。比如:使用documentFragment对象操作内存中的DOM,先把DOM给display:none(有一个reflow),然后你想怎么改就怎么改。比如修改100次,再显示。将一个DOM节点克隆到内存中,然后根据需要对其进行修改。修改好后,和网上的交换一下。3)不要把DOM节点的属性值作为循环中的变量放在循环中。否则会导致对该节点属性的大量读写。4)尽可能修改下层DOM。当然,改变下层的DOM可能会造成大面积的回流,但也可能影响小面积。5)对动画HTML元素使用固定或绝对位置,然后修改它们的CSS将不会回流。6)切勿使用表格布局。因为一个小小的改动就可能导致整个表格重新布局。几个工具,几篇文章有时候,你可能会发现,在IE下,你不知道自己修改了什么,结果CPU突然飙到100%,然后repaint/reflow花了好几秒补充一下,这种事情在IE时代经常发生。因此,我们需要一些工具来帮助我们查看我们的代码中是否有任何不合适的地方。在Chrome下,谷歌的SpeedTracer是一项非常强大的工作,可以让你看到你的浏览成本被渲染了多少。事实上,Safari和Chrome都可以在开发者工具中使用Timeline的东西。Firefox下基于Firebug的插件叫FirebugPaintEvents也不错。对于IE,您可以使用名为dynaTrace的IE扩展。最后,不要忘记以下关于提高浏览器性能的文章:Google–WebPerformanceBestPracticesYahoo–BestPracticesforSpeedingYourWebSiteSteveSouders–14RulesforFaster-LoadingWebSites原文链接:http://coolshell。cn/articles/9666.html