1)這篇文章太長了,閱讀成本太大,不能一口氣讀完。
2)花了大力氣讀了這篇文章后可以了解很多,但似乎對工作沒什么幫助。
所以,我準備寫下這篇文章來解決上述兩個問題。希望你能在上班途中,或是坐馬桶時就能讀完,并能從中學(xué)會一些能用在工作上的東西。
廢話少說,先來看個圖:
從上面這個圖中,我們可以看到那么幾個事:
1)瀏覽器會解析三個東西:
2)解析完成后,瀏覽器引擎會通過DOM Tree 和 CSS Rule Tree 來構(gòu)造 Rendering Tree。注意:
3)最后通過調(diào)用操作系統(tǒng)Native GUI的API繪制。
HTML的DOM Tree解析如下:
1
2
3
4
5
6
7
8
9
10
11
12
< 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會解析成這樣:
下面是另一個有SVG標簽的情況。
CSS的解析大概是下面這個樣子(下面主要說的是Gecko也就是Firefox的玩法),假設(shè)我們有下面的HTML文檔:
1
2
3
4
5
6
7
8
9
< 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是這個樣子:
然后我們的CSS文檔是這樣的:
1
2
3
4
/* 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會是這個樣子:
注意,圖中的第4條規(guī)則出現(xiàn)了兩次,一次是獨立的,一次是在規(guī)則3的子結(jié)點。所以,我們可以知道,建立CSS Rule Tree是需要比照著DOM Tree來的。CSS匹配DOM Tree主要是從右到左解析CSS的Selector,好多人以為這個事會比較快,其實并不一定。關(guān)鍵還看我們的CSS的Selector怎么寫了。
注意:CSS匹配HTML元素是一個相當復(fù)雜和有性能問題的事情。所以,你就會在N多地方看到很多人都告訴你,DOM樹要小,CSS盡量用id和class,千萬不要過渡層疊下去,……
通過這兩個樹,我們可以得到一個叫Style Context Tree,也就是下面這樣(把CSS Rule結(jié)點Attach到DOM Tree上):
所以,F(xiàn)irefox基本上來說是通過CSS 解析 生成 CSS Rule Tree,然后,通過比對DOM生成Style Context Tree,然后Firefox通過把Style Context Tree和其Render Tree(Frame Tree)關(guān)聯(lián)上,就完成了。注意:Render Tree會把一些不可見的結(jié)點去除掉。而Firefox中所謂的Frame就是一個DOM結(jié)點,不要被其名字所迷惑了。
注:Webkit不像Firefox要用兩個樹來干這個,Webkit也有Style對象,它直接把這個Style對象存在了相應(yīng)的DOM結(jié)點上了。
渲染的流程基本上如下(黃色的四個步驟):
注意:上圖流程中有很多連接線,這表示了Javascript動態(tài)修改了DOM屬性或是CSS屬會導(dǎo)致重新Layout,有些改變不會,就是那些指到天上的箭頭,比如,修改后的CSS rule沒有被匹配到,等。
這里重要要說兩個概念,一個是Reflow,另一個是Repaint。這兩個不是一回事。
下面是一個打開Wikipedia時的Layout/reflow的視頻(注:HTML在初始化的時候也會做一次reflow,叫intial reflow),你可以感受一下:
Reflow的成本比Repaint的成本高得多的多。DOM Tree里的每個結(jié)點都會有reflow方法,一個結(jié)點的reflow很有可能導(dǎo)致子結(jié)點,甚至父點以及同級結(jié)點的reflow。在一些高性能的電腦上也許還沒什么,但是如果reflow發(fā)生在手機上,那么這個過程是非常痛苦和耗電的。
所以,下面這些動作有很大可能會是成本比較高的。
注:display:none會觸發(fā)reflow,而visibility:hidden只會觸發(fā)repaint,因為沒有發(fā)現(xiàn)位置變化。
多說兩句關(guān)于滾屏的事,通常來說,如果在滾屏的時候,我們的頁面上的所有的像素都會跟著滾動,那么性能上沒什么問題,因為我們的顯卡對于這種把全屏像素往上往下移的算法是很快。但是如果你有一個fixed的背景圖,或是有些Element不跟著滾動,有些Elment是動畫,那么這個滾動的動作對于瀏覽器來說會是相當相當痛苦的一個過程。你可以看到很多這樣的網(wǎng)頁在滾動的時候性能有多差。因為滾屏也有可能會造成reflow。
基本上來說,reflow有如下的幾個原因:
好了,我們來看一個示例吧:
1
2
3
4
5
6
7
8
9
10
11
12
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!' ));
當然,我們的瀏覽器是聰明的,它不會像上面那樣,你每改一次樣式,它就reflow或repaint一次。一般來說,瀏覽器會把這樣的操作積攢一批,然后做一次reflow,這又叫異步reflow或增量異步reflow。但是有些情況瀏覽器是不會這么做的,比如:resize窗口,改變了頁面默認的字體,等。對于這些操作,瀏覽器會馬上進行reflow。
但是有些時候,我們的腳本會阻止瀏覽器這么干,比如:如果我們請求下面的一些DOM值:
因為,如果我們的程序需要這些值,那么瀏覽器需要返回最新的值,而這樣一樣會flush出去一些樣式的改變,從而造成頻繁的reflow/repaint。
下面是一些Best Practices:
1)不要一條一條地修改DOM的樣式。與其這樣,還不如預(yù)先定義好css的class,然后修改DOM的className。
1
2
3
4
5
6
7
8
9
10
11
// 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離線后修改。如:
3)不要把DOM結(jié)點的屬性值放在一個循環(huán)里當成循環(huán)里的變量。不然這會導(dǎo)致大量地讀寫這個結(jié)點的屬性。
4)盡可能的修改層級比較低的DOM。當然,改變層級比較底的DOM有可能會造成大面積的reflow,但是也可能影響范圍很小。
5)為動畫的HTML元件使用fixed或absoult的position,那么修改他們的CSS是不會reflow的。
6)千萬不要使用table布局。因為可能很小的一個小改動會造成整個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
有時候,你會也許會發(fā)現(xiàn)在IE下,你不知道你修改了什么東西,結(jié)果CPU一下子就上去了到100%,然后過了好幾秒鐘repaint/reflow才完成,這種事情以IE的年代時經(jīng)常發(fā)生。所以,我們需要一些工具幫我們看看我們的代碼里有沒有什么不合適的東西。
最后,別忘了下面這幾篇提高瀏覽器性能的文章:
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:0731-84117792 E-MAIL:11247931@qq.com