1.背景介紹
瀏覽器渲染頁面的大致過程:
從瀏覽器地址欄的請(qǐng)求鏈接開始,瀏覽器通過DNS解析查到域名映射的IP地址,成功之后瀏覽器端向此IP地址取得連接,成功連接之后,瀏覽器端將請(qǐng)求頭信息 通過HTTP協(xié)議向此IP地址所在服務(wù)器發(fā)起請(qǐng)求,服務(wù)器接受到請(qǐng)求之后等待處理,最后向?yàn)g覽器端發(fā)回響應(yīng),此時(shí)在HTTP協(xié)議下,瀏覽器從服務(wù)器接收到 text/html類型的代碼,瀏覽器開始顯示此html,并獲取其中內(nèi)嵌資源地址,然后瀏覽器再發(fā)起請(qǐng)求來獲取這些資源,并在瀏覽器的html中顯示
2.知識(shí)剖析
瀏覽器解析的大概的工作流程可以歸納為以下幾個(gè)步驟
1. 用戶輸入網(wǎng)址(假設(shè)是個(gè) HTML 頁面,第一次訪問,無緩存情況),瀏覽器向服務(wù)器發(fā)出HTTP請(qǐng)求,服務(wù)器返回 HTML 文件; (善用緩存,減少HTTP請(qǐng)求,減輕服務(wù)器壓力)
2. 瀏覽器載入 HTML 代碼,發(fā)現(xiàn) head 內(nèi)有一個(gè) link 引用外部 CSS 文件,則瀏覽器立即發(fā)送CSS文件請(qǐng)求,獲取瀏覽器返回的CSS文件;? (CSS文件合并,減少HTTP請(qǐng)求)
3. 瀏覽器繼續(xù)載入 HTML 中 body 部分的代碼,并且 CSS 文件已經(jīng)拿到手了,可以開始渲染頁面了;CSS文件需要放置最上面,避免網(wǎng)頁重新渲染。
4. 瀏覽器在代碼中發(fā)現(xiàn)一個(gè) img 標(biāo)簽引用了一張圖片,向服務(wù)器發(fā)出請(qǐng)求。此時(shí)瀏覽器不會(huì)等到圖片下載完,而是繼續(xù)渲染后面的代碼;(圖片文件合并,減少HTTP請(qǐng)求)
5. 服務(wù)器返回圖片文件,由于圖片占用了一定面積,影響了后面段落的排布,因此瀏覽器需要回過頭來重新渲染這部分代碼;? (最好圖片都設(shè)置尺寸,避免重新渲染)
6. 瀏覽器發(fā)現(xiàn)了一個(gè)包含一行 JavaScript 代碼的 script? 標(biāo)簽,會(huì)立即運(yùn)行該js代碼;(script最好放置頁面最下面)
7.js腳本執(zhí)行了語句,它令瀏覽器隱藏掉代碼中的某個(gè) div,突然就少了一個(gè)元素,瀏覽器不得不重新渲染這部分代碼;? (頁面初始化樣式不要使用js控制)
8.終于等到了 /html 的到來,瀏覽器淚流滿面……
9. 當(dāng)用戶點(diǎn)了一下界面中的“換膚”按鈕,JavaScript 讓瀏覽器換了一下 link 標(biāo)簽的 CSS 路徑;
10. 瀏覽器召集了在座的各位 div span ul li 們,“大伙兒收拾收拾行李,咱得重新來過……”,瀏覽器向服務(wù)器請(qǐng)求了新的CSS文件,重新渲染頁面。
瀏覽器每天就這么來來回回跑著,要知道不同的人寫出來的 HTML 和 CSS 代碼質(zhì)量參差不齊,說不定哪天跑著跑著就掛掉了。
好在這個(gè)世界還有這么一群人——頁面重構(gòu)工程師,平時(shí)挺不起眼,也就幫視覺設(shè)計(jì)師們切切圖啊改改字,其實(shí)背地里還是干了不少實(shí)事的。
3.常見問題
什么是repain(重繪)和reflow(回流)?
4.解決方案
說到頁面為什么會(huì)慢?那是因?yàn)闉g覽器要花時(shí)間、花精力去渲染,尤其是當(dāng)它發(fā)現(xiàn)某個(gè)部分發(fā)生了點(diǎn)變化影響了布局,需要倒回去重新渲染, 該過程稱為reflow(回流)。
reflow 幾乎是無法避免的?,F(xiàn)在界面上流行的一些效果,比如樹狀目錄的折疊、展開(實(shí)質(zhì)上是元素的顯 示與隱藏)等,都將引起瀏覽器的 reflow。鼠標(biāo)滑過、點(diǎn)擊……只要這些行為引起了頁面上某些元素的占位面積、定位方式、邊距等屬性的變化,都會(huì)引起它內(nèi)部、周圍甚至整個(gè)頁面的重新渲 染。通常我們都無法預(yù)估瀏覽器到底會(huì) reflow 哪一部分的代碼,它們都彼此相互影響著。
如果只是改變某個(gè)元素的背景色、文 字顏色、邊框顏色等等不影響它周圍或內(nèi)部布局的屬性,將只會(huì)引起瀏覽器 repaint(重繪)。
repaint 的速度明顯快于 reflow(在IE下需要換一下說法,reflow 要比 repaint 更緩慢)。
為什么reflow 要比 repaint 更緩慢?
repaint(重繪) ,repaint發(fā)生更改時(shí),元素的外觀被改變,且在沒有改變布局的情況下發(fā)生,如改變outline,visibility,background color,不會(huì)影響到dom結(jié)構(gòu)渲染。
reflow(回流),與repaint區(qū)別就是他會(huì)影響到dom的結(jié)構(gòu)渲染,同時(shí)他會(huì)觸發(fā)repaint,他會(huì)改變他本身與所有父輩元素(祖先),這種開銷是非常昂貴的,導(dǎo)致性能下降是必然的,頁面元素越多效果越明顯。
注意:回流必將引起重繪,而重繪不一定會(huì)引起回流。
5.編碼實(shí)戰(zhàn)
6.擴(kuò)展思考
引起repain(重繪)和reflow(回流)的一些操作?
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。
當(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)的時(shí)候。
當(dāng)你修改網(wǎng)頁的默認(rèn)字體時(shí)。
當(dāng)你設(shè)置 style 屬性的值 (Setting a property of the style attribute)。
注:display:none 會(huì)觸發(fā) reflow,而 visibility:hidden 只會(huì)觸發(fā) repaint,因?yàn)闆]有發(fā)現(xiàn)位置變化。
7.參考文獻(xiàn)
參考一:reflow(回流)和repaint(重繪)及其優(yōu)化
8.更多討論
如何減少repain(重繪)和reflow(回流)?
reflow是不可避免的,只能將reflow對(duì)性能的影響減到最小,給出下面幾條建議:
1. 不要一條一條地修改 DOM 的樣式。通過設(shè)置style屬性改變結(jié)點(diǎn)樣式的話,每設(shè)置一次都會(huì)導(dǎo)致一次reflow。所以最好通過設(shè)置class的方式,這樣可以將多次改變樣式屬性的操作合并成一次操作。
2. 讓要操作的元素進(jìn)行”離線處理”,處理完后一起更新;
- 使用DocumentFragment進(jìn)行緩存操作,引發(fā)一次回流和重繪;
var fragment = document.createDocumentFragment();
fragment.appendChild(document.createTextNode('keenboy test 111'));
fragment.appendChild(document.createElement('br'));
fragment.appendChild(document.createTextNode('keenboy test 222'));
document.body.appendChild(fragment);
- 使用display:none技術(shù),只引發(fā)兩次回流和重繪;
原理:由于display屬性為none的元素不在渲染樹中,對(duì)隱藏的元素操 作不會(huì)引發(fā)其他元素的重排。如果要對(duì)一個(gè)元素進(jìn)行復(fù)雜的操作時(shí),可以先隱藏它,操作完成后再顯示。這樣只在隱藏和顯示時(shí)觸發(fā)2次重排。
3.設(shè)置元素的position為absolute或fixed;
元素脫離標(biāo)準(zhǔn)文檔流,也從DOM樹結(jié)構(gòu)中脫離出來,在需要reflow時(shí)只需要reflow自身與下級(jí)元素。
4.不要用tables布局;
tables中某個(gè)元素一旦觸發(fā)reflow就會(huì)導(dǎo)致table里所有的其它元素reflow。在適合用table的場(chǎng)合,可以設(shè)置table-layout為auto或fixed,這樣可以讓table一行一行的渲染,這種做法也是為了限制reflow的影響范圍。
5.避免使用CSS的JavaScript表達(dá)式,如果css里有expression,每次都會(huì)重新計(jì)算一遍。
鳴謝