早速、画像を見てみましょう:
上の画像から、いくつかのことがわかります:
1) ブラウザは 3 つのことを解析します:
1 つは実際、Webkit には、これら 3 種類のドキュメントに対応する 3 つの C++ クラスがあります。これら 3 つのファイルを解析すると、DOM ツリーが生成されます。 CSS、CSS を解析すると CSS ルール ツリーが生成されます。 Javascript、スクリプトは主に DOM API と CSSOM API を通じて DOM ツリーと CSS ルール ツリーを操作します
2) 解析が完了すると、ブラウザ エンジンは DOM ツリーと CSS ルール ツリーを通じてレンダリング ツリーを構築します。注:
レンダリング ツリー Header や display:none などの一部のものをレンダリング ツリーに配置する必要がないため、レンダリング ツリーは DOM ツリーと同等ではありません。 CSS ルール ツリーの主な目的は、マッチングを完了し、レンダリング ツリー上の各要素に CSS ルールを添付することです。つまり、DOM ノードです。フレームとも言います。 次に、各フレーム (つまり、各要素) の位置を計算します。これは、レイアウトおよびリフロー プロセスとも呼ばれます。
3) 最後に、オペレーティング システムのネイティブ GUI の API を呼び出して描画します。
DOM 解析 HTML の DOM ツリー解析は次のとおりです:
<html><html><head> <title>Web page parsing</title></head><body> <div> <h1>Web page parsing</h1> <p>This is an example Web page.</p> </div></body></html>
上記の HTML は次のように解析されます:
CSS の解析はおそらく次のようになります (以下は主に Gecko について話します、 Firefox です) 再生方法)、次の HTML ドキュメントがあると仮定します:
<doc><title>A few quotes</title><para> Franklin said that <quote>"A penny saved is a penny earned."</quote></para><para> FDR said <quote>"We have nothing to fear but <span>fear itself.</span>"</quote></para></doc>
DOM ツリーは次のようになります:
CSS ドキュメントは次のようになります:
/* rule 1 */ doc { display: block; text-indent: 1em; }/* rule 2 */ title { display: block; font-size: 3em; }/* rule 3 */ para { display: block; }/* rule 4 */ [class="emph"] { font-style: italic; }
CSS ルール ツリーは次のようになります。次のようになります:
ルール 4 は図内に 2 回表示され、1 回は独立して、もう 1 回はルール 3 の子ノードとして表示されていることに注意してください。したがって、CSS ルール ツリーの確立は DOM ツリーに基づいて行う必要があることがわかります。 CSS マッチング DOM ツリーは主に CSS のセレクターを右から左に解析する方が高速であると多くの人が考えていますが、実際には必ずしもそうではありません。キーは、CSS セレクターの記述方法によって異なります。
注: HTML 要素の CSS マッチングは、パフォーマンスの問題を伴うかなり複雑な問題です。したがって、DOM ツリーは小さくすべきで、CSS はできる限り ID とクラスを使用し、オーバーレイしないでください...
これら 2 つのツリーを通じて、これはスタイル コンテキスト ツリーと呼ばれ、次のようなものです (CSS ルール ノードを DOM ツリーにアタッチします):
つまり、Firefox は基本的に CSS 解析を通じて CSS ルール ツリーを生成し、DOM を比較してスタイル コンテキストを生成します。ツリーを作成し、Firefox がスタイル コンテキスト ツリーをそのレンダー ツリー (フレーム ツリー) に関連付けることでそれを完成させます。注: Render Tree は、いくつかの非表示のノードを削除します。 Firefox のいわゆる Frame は DOM ノードです。その名前に混同しないでください。
注: Firefox とは異なり、Webkit はこれを行うために 2 つのツリーを使用し、対応する DOM ノードに Style オブジェクトを直接保存します。
レンダリングプロセスは基本的に次のとおりです(黄色の4つのステップ):
注: 上記のプロセスには多くの接続線があります。つまり、JavaScript による DOM 属性または CSS プロパティの動的変更により、再レイアウトが発生し、これらは空を指す矢印です。たとえば、変更された CSS ルールが一致しないなどです。
ここで説明する重要な概念が 2 つあります。1 つはリフロー、もう 1 つはリペイントです。これら 2 つは同じものではありません。
リフローのコストはリペイントのコストよりもはるかに高くなります。 DOM ツリー内の各ノードにはリフロー メソッドがあり、ノードのリフローは、子ノード、さらには親ノードや兄弟ノードのリフローにつながる可能性があります。一部の高性能コンピュータでは問題にならない場合もありますが、携帯電話でリフローが発生すると、そのプロセスは非常に困難で電力を消費します。 したがって、次のアクションは比較的コストがかかる可能性があります。
多说两句关于滚屏的事,通常来说,如果在滚屏的时候,我们的页面上的所有的像素都会跟着滚动,那么性能上没什么问题,因为我们的显卡对于这种把全屏像素往上往下移的算法是很快。但是如果你有一个fixed的背景图,或是有些Element不跟着滚动,有些Elment是动画,那么这个滚动的动作对于浏览器来说会是相当相当痛苦的一个过程。你可以看到很多这样的网页在滚动的时候性能有多差。因为滚屏也有可能会造成reflow。
基本上来说,reflow有如下的几个原因:
好了,我们来看一个示例吧:
var bstyle = document.body.style; // cachebstyle.padding = "20px"; // reflow, repaintbstyle.border = "10px solid red"; // 再一次的 reflow 和 repaintbstyle.color = "blue"; // repaintbstyle.backgroundColor = "#fad"; // repaintbstyle.fontSize = "2em"; // reflow, repaint// new DOM element - reflow, repaintdocument.body.appendChild(document.createTextNode('dude!'));
当然,我们的浏览器是聪明的,它不会像上面那样,你每改一次样式,它就reflow或repaint一次。一般来说,浏览器会把这样的操作积攒一批,然后做一次reflow,这又叫异步reflow或增量异步reflow。但是有些情况浏览器是不会这么做的,比如:resize窗口,改变了页面默认的字体,等。对于这些操作,浏览器会马上进行reflow。
但是有些时候,我们的脚本会阻止浏览器这么干,比如:如果我们请求下面的一些DOM值:
offsetTop, offsetLeft, offsetWidth, offsetHeightscrollTop/Left/Width/HeightclientTop/Left/Width/HeightIE中的 getComputedStyle(), 或 currentStyle
因为,如果我们的程序需要这些值,那么浏览器需要返回最新的值,而这样一样会flush出去一些样式的改变,从而造成频繁的reflow/repaint。
下面是一些Best Practices:
1)不要一条一条地修改DOM的样式。与其这样,还不如预先定义好css的class,然后修改DOM的className。
// badvar left = 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次,然后再把他显示出来。 clone一个DOM结点到内存里,然后想怎么改就怎么改,改完后,和在线的那个的交换一下。
3)不要把DOM结点的属性值放在一个循环里当成循环里的变量。不然这会导致大量地读写这个结点的属性。
4)尽可能的修改层级比较低的DOM。当然,改变层级比较底的DOM有可能会造成大面积的reflow,但是也可能影响范围很小。
5)为动画的HTML元件使用fixed或absoult的position,那么修改他们的CSS是不会reflow的。
6)千万不要使用table布局。因为可能很小的一个小改动会造成整个table的重新布局。
有时候,你会也许会发现在IE下,你不知道你修改了什么东西,结果CPU一下子就上去了到100%,然后过了好几秒钟repaint/reflow才完成,这种事情以IE的年代时经常发生。所以,我们需要一些工具帮我们看看我们的代码里有没有什么不合适的东西。
Chrome下,Google的SpeedTracer是个非常强悍的工作让你看看你的浏览渲染的成本有多大。其实Safari和Chrome都可以使用开发者工具里的一个Timeline的东东。 Firefox下这个基于Firebug的叫Firebug Paint Events的插件也不错。 IE下你可以用一个叫dynaTrace的IE扩展。 最后,别忘了下面这几篇提高浏览器性能的文章: