1.背景介紹
瀏覽器渲染頁(yè)面的大致過(guò)程:
從瀏覽器地址欄的請(qǐng)求鏈接開(kāi)始,瀏覽器通過(guò)DNS解析查到域名映射的IP地址抱虐,成功之后瀏覽器端向此IP地址取得連接昌阿,成功連接之后,瀏覽器端將請(qǐng)求頭信息 通過(guò)HTTP協(xié)議向此IP地址所在服務(wù)器發(fā)起請(qǐng)求恳邀,服務(wù)器接受到請(qǐng)求之后等待處理懦冰,最后向?yàn)g覽器端發(fā)回響應(yīng),此時(shí)在HTTP協(xié)議下谣沸,瀏覽器從服務(wù)器接收到 text/html類型的代碼刷钢,瀏覽器開(kāi)始顯示此html,并獲取其中內(nèi)嵌資源地址乳附,然后瀏覽器再發(fā)起請(qǐng)求來(lái)獲取這些資源内地,并在瀏覽器的html中顯示
2.知識(shí)剖析
瀏覽器解析的大概的工作流程可以歸納為以下幾個(gè)步驟
1. 用戶輸入網(wǎng)址(假設(shè)是個(gè) HTML 頁(yè)面,第一次訪問(wèn)赋除,無(wú)緩存情況)阱缓,瀏覽器向服務(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)拿到手了,可以開(kāi)始渲染頁(yè)面了键畴;CSS文件需要放置最上面最盅,避免網(wǎng)頁(yè)重新渲染。
4. 瀏覽器在代碼中發(fā)現(xiàn)一個(gè) img 標(biāo)簽引用了一張圖片起惕,向服務(wù)器發(fā)出請(qǐng)求涡贱。此時(shí)瀏覽器不會(huì)等到圖片下載完,而是繼續(xù)渲染后面的代碼惹想;(圖片文件合并问词,減少HTTP請(qǐng)求)
5. 服務(wù)器返回圖片文件,由于圖片占用了一定面積嘀粱,影響了后面段落的排布激挪,因此瀏覽器需要回過(guò)頭來(lái)重新渲染這部分代碼;? (最好圖片都設(shè)置尺寸锋叨,避免重新渲染)
6. 瀏覽器發(fā)現(xiàn)了一個(gè)包含一行 JavaScript 代碼的 script? 標(biāo)簽垄分,會(huì)立即運(yùn)行該js代碼;(script最好放置頁(yè)面最下面)
7.js腳本執(zhí)行了語(yǔ)句娃磺,它令瀏覽器隱藏掉代碼中的某個(gè) div,突然就少了一個(gè)元素薄湿,瀏覽器不得不重新渲染這部分代碼;? (頁(yè)面初始化樣式不要使用js控制)
8.終于等到了 /html 的到來(lái)偷卧,瀏覽器淚流滿面……
9. 當(dāng)用戶點(diǎn)了一下界面中的“換膚”按鈕豺瘤,JavaScript 讓瀏覽器換了一下 link 標(biāo)簽的 CSS 路徑;
10. 瀏覽器召集了在座的各位 div span ul li 們听诸,“大伙兒收拾收拾行李坐求,咱得重新來(lái)過(guò)……”,瀏覽器向服務(wù)器請(qǐng)求了新的CSS文件晌梨,重新渲染頁(yè)面桥嗤。
瀏覽器每天就這么來(lái)來(lái)回回跑著,要知道不同的人寫(xiě)出來(lái)的 HTML 和 CSS 代碼質(zhì)量參差不齊仔蝌,說(shuō)不定哪天跑著跑著就掛掉了砸逊。
好在這個(gè)世界還有這么一群人——頁(yè)面重構(gòu)工程師,平時(shí)挺不起眼掌逛,也就幫視覺(jué)設(shè)計(jì)師們切切圖啊改改字师逸,其實(shí)背地里還是干了不少實(shí)事的。
3.常見(jiàn)問(wèn)題
什么是repain(重繪)和reflow(回流)豆混?
4.解決方案
說(shuō)到頁(yè)面為什么會(huì)慢篓像?那是因?yàn)闉g覽器要花時(shí)間、花精力去渲染皿伺,尤其是當(dāng)它發(fā)現(xiàn)某個(gè)部分發(fā)生了點(diǎn)變化影響了布局员辩,需要倒回去重新渲染, 該過(guò)程稱為reflow(回流)鸵鸥。
reflow 幾乎是無(wú)法避免的〉旎現(xiàn)在界面上流行的一些效果丹皱,比如樹(shù)狀目錄的折疊、展開(kāi)(實(shí)質(zhì)上是元素的顯 示與隱藏)等宋税,都將引起瀏覽器的 reflow摊崭。鼠標(biāo)滑過(guò)、點(diǎn)擊……只要這些行為引起了頁(yè)面上某些元素的占位面積杰赛、定位方式呢簸、邊距等屬性的變化,都會(huì)引起它內(nèi)部乏屯、周圍甚至整個(gè)頁(yè)面的重新渲 染根时。通常我們都無(wú)法預(yù)估瀏覽器到底會(huì) reflow 哪一部分的代碼,它們都彼此相互影響著辰晕。
如果只是改變某個(gè)元素的背景色蛤迎、文 字顏色、邊框顏色等等不影響它周圍或內(nèi)部布局的屬性含友,將只會(huì)引起瀏覽器 repaint(重繪)忘苛。
repaint 的速度明顯快于 reflow(在IE下需要換一下說(shuō)法,reflow 要比 repaint 更緩慢)唱较。
為什么reflow 要比 repaint 更緩慢扎唾?
repaint(重繪) ,repaint發(fā)生更改時(shí)南缓,元素的外觀被改變胸遇,且在沒(méi)有改變布局的情況下發(fā)生,如改變outline,visibility,background color汉形,不會(huì)影響到dom結(jié)構(gòu)渲染纸镊。
reflow(回流),與repaint區(qū)別就是他會(huì)影響到dom的結(jié)構(gòu)渲染概疆,同時(shí)他會(huì)觸發(fā)repaint逗威,他會(huì)改變他本身與所有父輩元素(祖先),這種開(kāi)銷是非常昂貴的岔冀,導(dǎo)致性能下降是必然的凯旭,頁(yè)面元素越多效果越明顯。
注意:回流必將引起重繪使套,而重繪不一定會(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)畫(huà)的時(shí)候。
當(dāng)你修改 /刪除CSS 樣式的時(shí)候谊娇。
當(dāng)你 Resize 窗口的時(shí)候肺孤,或是滾動(dòng)的時(shí)候罗晕。
當(dāng)你修改網(wǎng)頁(yè)的默認(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)闆](méi)有發(fā)現(xiàn)位置變化。
7.參考文獻(xiàn)
參考一:reflow(回流)和repaint(重繪)及其優(yōu)化
參考二:瀏覽器加載渲染網(wǎng)頁(yè)過(guò)程解析酬屉?
8.更多討論
如何減少repain(重繪)和reflow(回流)半等?
reflow是不可避免的,只能將reflow對(duì)性能的影響減到最小,給出下面幾條建議:
1. 不要一條一條地修改 DOM 的樣式呐萨。通過(guò)設(shè)置style屬性改變結(jié)點(diǎn)樣式的話杀饵,每設(shè)置一次都會(huì)導(dǎo)致一次reflow。所以最好通過(guò)設(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的元素不在渲染樹(shù)中北秽,對(duì)隱藏的元素操 作不會(huì)引發(fā)其他元素的重排葡幸。如果要對(duì)一個(gè)元素進(jìn)行復(fù)雜的操作時(shí),可以先隱藏它贺氓,操作完成后再顯示蔚叨。這樣只在隱藏和顯示時(shí)觸發(fā)2次重排。
3.設(shè)置元素的position為absolute或fixed辙培;
元素脫離標(biāo)準(zhǔn)文檔流缅叠,也從DOM樹(shù)結(jié)構(gòu)中脫離出來(lái),在需要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ì)算一遍。
鳴謝