
1)瀏覽器會(huì)解析三個(gè)東西:
一個(gè)是HTML/SVG/XHTML,事實(shí)上,Webkit有三個(gè)C++的類對(duì)應(yīng)這三類文檔。解析這三種文件會(huì)產(chǎn)生一個(gè)DOM Tree。(上圖中的DOM區(qū)塊)
CSS,解析CSS會(huì)產(chǎn)生CSS規(guī)則樹。(上圖中的CSSDOM區(qū)塊)
Javascript,腳本,主要是通過DOM API和CSSOM API來操作DOM Tree和CSS Rule Tree.(上圖中的RENDER TREE區(qū)塊)
2)解析完成后,瀏覽器引擎會(huì)通過DOM Tree 和 CSS Rule Tree 來構(gòu)造 Rendering Tree。注意:
- Rendering Tree 渲染樹并不等同于DOM樹,因?yàn)橐恍┫馠eader或display:none的東西就沒必要放在渲染樹中了。
- CSS 的 Rule Tree主要是為了完成匹配并把CSS Rule附加上Rendering Tree上的每個(gè)Element。也就是DOM結(jié)點(diǎn)。也就是所謂的Frame。建立CSS Rule Tree是需要比照著DOM Tree來的。
- 計(jì)算每個(gè)Frame(也就是每個(gè)Element)的位置,這又叫l(wèi)ayout和reflow過程。
3)最后通過調(diào)用操作系統(tǒng)Native GUI的API繪制。
注意:CSS匹配HTML元素是一個(gè)相當(dāng)復(fù)雜和有性能問題的事情。所以,你就會(huì)在N多地方看到很多人都告訴你,DOM樹要小,CSS盡量用id和class,千萬不要過渡層疊下去
渲染
- 計(jì)算CSS樣式
構(gòu)建Render Tree
Layout – 定位坐標(biāo)和大小,是否換行,各種position, overflow, z-index屬性 ……
正式開畫

注意:上圖流程中有很多連接線,這表示了Javascript動(dòng)態(tài)修改了DOM屬性或是CSS屬會(huì)導(dǎo)致重新Layout,有些改變不會(huì),就是那些指到天上的箭頭,比如,修改后的CSS rule沒有被匹配到,等。
這里重要要說兩個(gè)概念,一個(gè)是Reflow,另一個(gè)是Repaint。這兩個(gè)不是一回事。
1.Repaint——屏幕的一部分要重畫,比如某個(gè)CSS的背景色變了。但是元素的幾何尺寸沒有變。
Reflow——意味著元件的幾何尺寸變了,我們需要重新驗(yàn)證并計(jì)算Render Tree。是Render Tree的一部分或全部發(fā)生了變化。這就是Reflow,或是Layout。(HTML使用的是flow based layout,也就是流式布局,所以,如果某元件的幾何尺寸發(fā)生了變化,需要重新布局,也就叫reflow)reflow 會(huì)從<html>這個(gè)root frame開始遞歸往下,依次計(jì)算所有的結(jié)點(diǎn)幾何尺寸和位置,在reflow過程中,可能會(huì)增加一些frame,比如一個(gè)文本字符串必需被包裝起來。
2.Reflow的成本比Repaint的成本高得多的多。DOM Tree里的每個(gè)結(jié)點(diǎn)都會(huì)有reflow方法,一個(gè)結(jié)點(diǎn)的reflow很有可能導(dǎo)致子結(jié)點(diǎn),甚至父點(diǎn)以及同級(jí)結(jié)點(diǎn)的reflow。在一些高性能的電腦上也許還沒什么,但是如果reflow發(fā)生在手機(jī)上,那么這個(gè)過程是非常痛苦和耗電的。
所以,下面這些動(dòng)作有很大可能會(huì)是成本比較高的。
- 當(dāng)你增加、刪除、修改DOM結(jié)點(diǎn)時(shí),會(huì)導(dǎo)致Reflow或Repaint
當(dāng)你移動(dòng)DOM的位置,或是搞個(gè)動(dòng)畫的時(shí)候。
當(dāng)你修改CSS樣式的時(shí)候。
當(dāng)你Resize窗口的時(shí)候(移動(dòng)端沒有這個(gè)問題),或是滾動(dòng)的時(shí)候。
當(dāng)你修改網(wǎng)頁(yè)的默認(rèn)字體時(shí)。
注:display:none會(huì)觸發(fā)reflow,而visibility:hidden只會(huì)觸發(fā)repaint,因?yàn)闆]有發(fā)現(xiàn)位置變化。
基本上來說,reflow有如下的幾個(gè)原因:
- Initial。網(wǎng)頁(yè)初始化的時(shí)候。
Incremental。一些Javascript在操作DOM Tree時(shí)。
Resize。其些元件的尺寸變了。
StyleChange。如果CSS的屬性發(fā)生變化了。
Dirty。幾個(gè)Incremental的reflow發(fā)生在同一個(gè)frame的子樹上。
示例
<pre>var bstyle = document.body.style; // cache
bstyle.padding = "20px"; // reflow, repaint
bstyle.border = "10px solid red"; // 再一次的 reflow 和 repaint
bstyle.color = "blue"; // repaint
bstyle.backgroundColor = "#fad"; // repaint
bstyle.fontSize = "2em"; // reflow, repaint
// new DOM element - reflow, repaint
document.body.appendChild(document.createTextNode('dude!'));</pre>
當(dāng)然,我們的瀏覽器是聰明的,它不會(huì)像上面那樣,你每改一次樣式,它就reflow或repaint一次。一般來說,瀏覽器會(huì)把這樣的操作積攢一批,然后做一次reflow,這又叫異步reflow或增量異步reflow。但是有些情況瀏覽器是不會(huì)這么做的,比如:resize窗口,改變了頁(yè)面默認(rèn)的字體,等。對(duì)于這些操作,瀏覽器會(huì)馬上進(jìn)行reflow。
減少reflow/repaint
1.不要一條一條地修改DOM的樣式。與其這樣,還不如預(yù)先定義好css的class,然后修改DOM的className。
2.把DOM離線后修改。
3.不要把DOM結(jié)點(diǎn)的屬性值放在一個(gè)循環(huán)里當(dāng)成循環(huán)里的變量。不然這會(huì)導(dǎo)致大量地讀寫這個(gè)結(jié)點(diǎn)的屬性。
4.盡可能的修改層級(jí)比較低的DOM。當(dāng)然,改變層級(jí)比較底的DOM有可能會(huì)造成大面積的reflow,但是也可能影響范圍很小。
5.為動(dòng)畫的HTML元件使用fixed或absoult的position,那么修改他們的CSS是不會(huì)reflow的。
6.千萬不要使用table布局。因?yàn)榭赡芎苄〉囊粋€(gè)小改動(dòng)會(huì)造成整個(gè)table的重新布局。
文章出處點(diǎn)這里