瀏覽器的渲染原理簡(jiǎn)介

看到這個(gè)標(biāo)題大家一定會(huì)想到這篇神文《How Browsers Work》算行,這篇文章把瀏覽器的很多細(xì)節(jié)講得很細(xì)里伯,而且也被翻譯成了中文蔗衡。為什么我還想寫(xiě)一篇呢再榄?因?yàn)閮蓚€(gè)原因
1) 這篇文章太長(zhǎng)了苗胀,閱讀成本太大襟诸,不能一口氣讀完。
2) 花了大力氣讀了這篇文章后可以了解很多基协,但似乎對(duì)工作沒(méi)什么幫助歌亲。
所以,我準(zhǔn)備寫(xiě)下這篇文章來(lái)解決上述兩個(gè)問(wèn)題澜驮。希望你能在上班途中陷揪,或是坐馬桶時(shí)就能讀完,并能從中學(xué)會(huì)一些能用在工作上的東西。

瀏覽器工作大流程

廢話(huà)少說(shuō)悍缠,先來(lái)看個(gè)圖:


瀏覽器工作大流程

從上面這個(gè)圖中卦绣,我們可以看到那么幾個(gè)事:

1) 瀏覽器會(huì)解析三個(gè)東西:
  • 一個(gè)是HTML/SVG/XHTML,事實(shí)上扮休,Webkit有三個(gè)C++的類(lèi)對(duì)應(yīng)這三類(lèi)文檔迎卤。解析這三種文件會(huì)產(chǎn)生一個(gè)DOM Tree。
  • CSS玷坠,解析CSS會(huì)產(chǎn)生CSS規(guī)則樹(shù)蜗搔。
  • Javascript,腳本八堡,主要是通過(guò)DOM API和CSSOM API來(lái)操作DOM Tree和CSS Rule Tree.
2) 解析完成后樟凄,瀏覽器引擎會(huì)通過(guò)DOM Tree 和 CSS Rule Tree 來(lái)構(gòu)造 Rendering Tree。注意:
  • Rendering Tree 渲染樹(shù)并不等同于DOM樹(shù)兄渺,因?yàn)橐恍┫馠eader或display:none的東西就沒(méi)必要放在渲染樹(shù)中了缝龄。
  • CSS 的 Rule Tree主要是為了完成匹配并把CSS Rule附加上Rendering Tree上的每個(gè)Element。也就是DOM結(jié)點(diǎn)挂谍。也就是所謂的Frame叔壤。
  • 然后,計(jì)算每個(gè)Frame(也就是每個(gè)Element)的位置口叙,這又叫l(wèi)ayout和reflow過(guò)程炼绘。
3)最后通過(guò)調(diào)用操作系統(tǒng)Native GUI的API繪制。

DOM解析

HTML的DOM Tree解析如下:

<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會(huì)解析成這樣:

DOM Tree

下面是另一個(gè)有SVG標(biāo)簽的情況妄田。


SVG Tree

CSS解析

CSS的解析大概是下面這個(gè)樣子(下面主要說(shuō)的是Gecko也就是Firefox的玩法)俺亮,假設(shè)我們有下面的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 Tree是這個(gè)樣子:


DOM Tree

然后我們的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 Rule Tree會(huì)是這個(gè)樣子:


CSS Rule Tree

注意,圖中的第4條規(guī)則出現(xiàn)了兩次疟呐,一次是獨(dú)立的脚曾,一次是在規(guī)則3的子結(jié)點(diǎn)。所以启具,我們可以知道本讥,建立CSS Rule Tree是需要比照著DOM Tree來(lái)的。CSS匹配DOM Tree主要是從右到左解析CSS的Selector富纸,好多人以為這個(gè)事會(huì)比較快囤踩,其實(shí)并不一定。關(guān)鍵還看我們的CSS的Selector怎么寫(xiě)了晓褪。

注意:CSS匹配HTML元素是一個(gè)相當(dāng)復(fù)雜和有性能問(wèn)題的事情堵漱。所以,你就會(huì)在N多地方看到很多人都告訴你涣仿,DOM樹(shù)要小勤庐,CSS盡量用id和class示惊,千萬(wàn)不要過(guò)渡層疊下去,……

通過(guò)這兩個(gè)樹(shù)愉镰,我們可以得到一個(gè)叫Style Context Tree米罚,也就是下面這樣(把CSS Rule結(jié)點(diǎn)Attach到DOM Tree上):

Style Context Tree

所以,F(xiàn)irefox基本上來(lái)說(shuō)是通過(guò)CSS 解析 生成 CSS Rule Tree丈探。然后录择,通過(guò)比對(duì)DOM生成Style Context Tree,然后Firefox通過(guò)把Style Context Tree和其Render Tree(Frame Tree)關(guān)聯(lián)上碗降,就完成了隘竭。
注意:Render Tree會(huì)把一些不可見(jiàn)的結(jié)點(diǎn)去除掉。而Firefox中所謂的Frame就是一個(gè)DOM結(jié)點(diǎn)讼渊,不要被其名字所迷惑了动看。

Style Contexts

注:Webkit不像Firefox要用兩個(gè)樹(shù)來(lái)干這個(gè),Webkit也有Style對(duì)象爪幻,它直接把這個(gè)Style對(duì)象存在了相應(yīng)的DOM結(jié)點(diǎn)上了菱皆。

渲染

渲染的流程基本上如下(黃色的四個(gè)步驟):
<b>
1.計(jì)算CSS樣式
2.構(gòu)建Render Tree
3.Layout – 定位坐標(biāo)和大小,是否換行挨稿,各種position, overflow, z-index屬性 ……
正式開(kāi)畫(huà)
4.正式開(kāi)畫(huà)
</b>

渲染的流程

注意:上圖流程中有很多連接線(xiàn)仇轻,這表示了Javascript動(dòng)態(tài)修改了DOM屬性或是CSS屬會(huì)導(dǎo)致重新Layout,有些改變不會(huì)奶甘,就是那些指到天上的箭頭拯田,比如,修改后的CSS rule沒(méi)有被匹配到甩十,等。
這里重要要說(shuō)兩個(gè)概念吭产,一個(gè)是Reflow侣监,另一個(gè)是Repaint。這兩個(gè)不是一回事臣淤。

  • Repaint——屏幕的一部分要重畫(huà)橄霉,比如某個(gè)CSS的背景色變了。但是元素的幾何尺寸沒(méi)有變邑蒋。

  • Reflow——意味著元件的幾何尺寸變了姓蜂,我們需要重新驗(yàn)證并計(jì)算Render Tree。是Render Tree的一部分或全部發(fā)生了變化医吊。這就是Reflow钱慢,或是Layout。(HTML使用的是flow based layout卿堂,也就是流式布局束莫,所以懒棉,如果某元件的幾何尺寸發(fā)生了變化,需要重新布局览绿,也就叫reflow)reflow 會(huì)從<html>這個(gè)root frame開(kāi)始遞歸往下策严,依次計(jì)算所有的結(jié)點(diǎn)幾何尺寸和位置,在reflow過(guò)程中饿敲,可能會(huì)增加一些frame妻导,比如一個(gè)文本字符串必需被包裝起來(lái)。

下面是一個(gè)打開(kāi)Wikipedia時(shí)的Layout/reflow的視頻(注:HTML在初始化的時(shí)候也會(huì)做一次reflow怀各,叫intial reflow)倔韭,你可以感受一下:
http://v.youku.com/v_show/id_XMzI5MDg0OTA0.html

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。在一些高性能的電腦上也許還沒(méi)什么沥曹,但是如果reflow發(fā)生在手機(jī)上份名,那么這個(gè)過(guò)程是非常痛苦和耗電的
所以妓美,下面這些動(dòng)作有很大可能會(huì)是成本比較高的僵腺。

  • 當(dāng)你增加、刪除壶栋、修改DOM結(jié)點(diǎn)時(shí)辰如,會(huì)導(dǎo)致Reflow或Repaint
  • 當(dāng)你移動(dòng)DOM的位置,或是搞個(gè)動(dòng)畫(huà)的時(shí)候贵试。
  • 當(dāng)你修改CSS樣式的時(shí)候琉兜。
  • 當(dāng)你Resize窗口的時(shí)候(移動(dòng)端沒(méi)有這個(gè)問(wèn)題),或是滾動(dòng)的時(shí)候毙玻。
  • 當(dāng)你修改網(wǎng)頁(yè)的默認(rèn)字體時(shí)豌蟋。

注:display:none會(huì)觸發(fā)reflow,而visibility:hidden只會(huì)觸發(fā)repaint桑滩,因?yàn)闆](méi)有發(fā)現(xiàn)位置變化梧疲。

多說(shuō)兩句關(guān)于滾屏的事,通常來(lái)說(shuō)运准,如果在滾屏的時(shí)候幌氮,我們的頁(yè)面上的所有的像素都會(huì)跟著滾動(dòng),那么性能上沒(méi)什么問(wèn)題胁澳,因?yàn)槲覀兊娘@卡對(duì)于這種把全屏像素往上往下移的算法是很快该互。但是如果你有一個(gè)fixed的背景圖,或是有些Element不跟著滾動(dòng)韭畸,有些Elment是動(dòng)畫(huà)慢洋,那么這個(gè)滾動(dòng)的動(dòng)作對(duì)于瀏覽器來(lái)說(shuō)會(huì)是相當(dāng)相當(dāng)痛苦的一個(gè)過(guò)程塘雳。你可以看到很多這樣的網(wǎng)頁(yè)在滾動(dòng)的時(shí)候性能有多差。因?yàn)闈L屏也有可能會(huì)造成reflow普筹。

基本上來(lái)說(shuō)败明,reflow有如下的幾個(gè)原因:

  • Initial。網(wǎng)頁(yè)初始化的時(shí)候太防。
  • Incremental妻顶。一些Javascript在操作DOM Tree時(shí)。
  • Resize蜒车。其些元件的尺寸變了讳嘱。
  • StyleChange。如果CSS的屬性發(fā)生變化了酿愧。
  • Dirty沥潭。幾個(gè)Incremental的reflow發(fā)生在同一個(gè)frame的子樹(shù)上。

好了嬉挡,我們來(lái)看一個(gè)示例吧:

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!'));

當(dāng)然钝鸽,我們的瀏覽器是聰明的,它不會(huì)像上面那樣庞钢,你每改一次樣式拔恰,它就reflow或repaint一次。一般來(lái)說(shuō)基括,瀏覽器會(huì)把這樣的操作積攢一批颜懊,然后做一次reflow,這又叫異步reflow或增量異步reflow风皿。但是有些情況瀏覽器是不會(huì)這么做的河爹,比如:resize窗口,改變了頁(yè)面默認(rèn)的字體桐款,等昌抠。對(duì)于這些操作,瀏覽器會(huì)馬上進(jìn)行reflow鲁僚。
但是有些時(shí)候,我們的腳本會(huì)阻止瀏覽器這么干裁厅,比如:如果我們請(qǐng)求下面的一些DOM值:

  • offsetTop, offsetLeft, offsetWidth, offsetHeight
  • scrollTop/Left/Width/Height
  • clientTop/Left/Width/Height
  • IE中的 getComputedStyle(), 或 currentStyle

因?yàn)楸常绻覀兊某绦蛐枰@些值,那么瀏覽器需要返回最新的值执虹,而這樣一樣會(huì)flush出去一些樣式的改變拓挥,從而造成頻繁的reflow/repaint。

減少reflow/repaint

下面是一些Best Practices:
1)不要一條一條地修改DOM的樣式袋励。與其這樣侥啤,還不如預(yù)先定義好css的class当叭,然后修改DOM的className。

// bad
var
left = 10,
top = 10;
el.style.left = left +"px";
el.style.top  = top  +"px";

// Good
el.className +=" theclassname";

// Good
el.style.cssText +="; left: "+ left +"px; top: "+ top +"px;";

2)把DOM離線(xiàn)后修改盖灸。如:

  • 使用documentFragment 對(duì)象在內(nèi)存里操作DOM
  • 先把DOM給display:none(有一次reflow)蚁鳖,然后你想怎么改就怎么改。比如修改100次赁炎,然后再把他顯示出來(lái)醉箕。
  • clone一個(gè)DOM結(jié)點(diǎn)到內(nèi)存里,然后想怎么改就怎么改率翅,改完后临谱,和在線(xiàn)的那個(gè)的交換一下趾唱。

3)不要把DOM結(jié)點(diǎn)的屬性值放在一個(gè)循環(huán)里當(dāng)成循環(huán)里的變量。不然這會(huì)導(dǎo)致大量地讀寫(xiě)這個(gè)結(jié)點(diǎn)的屬性己英。
4)盡可能的修改層級(jí)比較低的DOM。當(dāng)然吴旋,改變層級(jí)比較底的DOM有可能會(huì)造成大面積的reflow损肛,但是也可能影響范圍很小。
5)為動(dòng)畫(huà)的HTML元件使用fixed或absoult的position邮府,那么修改他們的CSS是不會(huì)reflow的荧关。
6)千萬(wàn)不要使用table布局。因?yàn)榭赡芎苄〉囊粋€(gè)小改動(dòng)會(huì)造成整個(gè)table的重新布局褂傀。

In this manner, the user agent can begin to lay out the table once the entire first row has been received. Cells in subsequent rows do not affect column widths. Any cell that has content that overflows uses the ‘overflow’ property to determine whether to clip the overflow content.
Fixed layout, CSS 2.1 Specification


This algorithm may be inefficient since it requires the user agent to have access to all the content in the table before determining the final layout and may demand more than one pass.
Automatic layout, CSS 2.1 Specification


幾個(gè)工具和幾篇文章

有時(shí)候忍啤,你會(huì)也許會(huì)發(fā)現(xiàn)在IE下,你不知道你修改了什么東西仙辟,結(jié)果CPU一下子就上去了到100%同波,然后過(guò)了好幾秒鐘repaint/reflow才完成,這種事情以IE的年代時(shí)經(jīng)常發(fā)生叠国。所以未檩,我們需要一些工具幫我們看看我們的代碼里有沒(méi)有什么不合適的東西。

  • Chrome下粟焊,Google的SpeedTracer是個(gè)非常強(qiáng)悍的工作讓你看看你的瀏覽渲染的成本有多大冤狡。其實(shí)Safari和Chrome都可以使用開(kāi)發(fā)者工具里的一個(gè)Timeline的東東。
  • Firefox下這個(gè)基于Firebug的叫Firebug Paint Events的插件也不錯(cuò)项棠。
  • IE下你可以用一個(gè)叫dynaTrace的IE擴(kuò)展悲雳。

最后,別忘了下面這幾篇提高瀏覽器性能的文章:

  • Google – Web Performance Best Practices
  • Yahoo – Best Practices for Speeding Up Your Web Site
  • Steve Souders – 14 Rules for Faster-Loading Web Sites

參考

  • David Baron的演講:Fast CSS: How Browsers Lay Out Web Pages:slideshow, all slides, audio (MP3), Session page, Lanyrd page

轉(zhuǎn)載

http://coolshell.cn/articles/9666.html

PS: 若你覺(jué)得可以香追、還行合瓢、過(guò)得去、甚至不太差的話(huà)透典,可以“關(guān)注”一下晴楔,就此謝過(guò)!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末顿苇,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子税弃,更是在濱河造成了極大的恐慌纪岁,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钙皮,死亡現(xiàn)場(chǎng)離奇詭異蜂科,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)短条,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)导匣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人茸时,你說(shuō)我怎么就攤上這事贡定。” “怎么了可都?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵缓待,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我渠牲,道長(zhǎng)旋炒,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任签杈,我火速辦了婚禮瘫镇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘答姥。我一直安慰自己铣除,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布鹦付。 她就那樣靜靜地躺著尚粘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪敲长。 梳的紋絲不亂的頭發(fā)上郎嫁,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音祈噪,去河邊找鬼泽铛。 笑死,一個(gè)胖子當(dāng)著我的面吹牛钳降,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播腌巾,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼遂填,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼铲觉!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起吓坚,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤撵幽,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后礁击,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體盐杂,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年哆窿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了链烈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挚躯,死狀恐怖强衡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情码荔,我是刑警寧澤漩勤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站缩搅,受9級(jí)特大地震影響越败,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜硼瓣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一究飞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧巨双,春花似錦噪猾、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至慢宗,卻和暖如春坪蚁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背镜沽。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工敏晤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缅茉。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓嘴脾,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子译打,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1)瀏覽器會(huì)解析三個(gè)東西:一個(gè)是HTML/SVG/XHTML耗拓,事實(shí)上,Webkit有三個(gè)C++的類(lèi)對(duì)應(yīng)這三類(lèi)文檔奏司。...
    24_Magic閱讀 283評(píng)論 0 2
  • 首先乔询,看一張圖片: 首先是解析:1)這是一個(gè)解析的過(guò)程,瀏覽器會(huì)解析三個(gè)東西:一個(gè)是HTML/SVG/XHTML韵洋,...
    osborne閱讀 362評(píng)論 0 0
  • 大家好搪缨,我是IT修真院深圳分院第01期學(xué)員食拜,一枚正直純潔善良的web程序員。 今天給大家分享一下勉吻,修真院官網(wǎng)CSS...
    嘴角那抹溫柔閱讀 1,868評(píng)論 0 5
  • 今夜月無(wú)痕监婶,往昔笑談聲。孤影醉無(wú)形齿桃,獨(dú)聞鳥(niǎo)蟲(chóng)音惑惶。
    童姥閱讀 340評(píng)論 0 0
  • 你一無(wú)所有就要拼带污。砰砰砰。你就重下香到,即便有上級(jí)鱼冀。
    205682e6ee8a閱讀 134評(píng)論 0 0