開發一個頁面時,不可避免的需要進行repaint和reflow。也就隻有以前的靜态頁面才會不存在repaint和reflow。repaint主要是針對某一個DOM元素進行的重繪,reflow則是回流,針對整個頁面的重排。字面意思來說:repaint就是重繪,reflow就是回流。repaint和reflow的目的是:展示一個新的頁面樣貌。在開始說明這兩個方法之前,我們先來看一下關于浏覽器從接收到一個URL到解析和渲染的相關流程。
1、浏覽器接收到URL到渲染完成的一個整體過程
假設是簡單的http請求(GET),IPV4,無代理。
1)浏覽器先檢視浏覽器緩存-系統緩存-路由器緩存,若緩存中有,請略過中間步驟,直接跳到第9步~若沒有,則按照下面的步驟進行操作。
2)浏覽器從url中解析出伺服器的主機名,并将主機名轉換成伺服器的IP位址。PS:DNS查找域名的過程
3)浏覽器從url中解析出端口号,預設80
4)浏覽器建立一條與伺服器的TCP連接配接(建立過程:三次握手)。PS:一個完整的TCP連接配接
5)浏覽器通過TCP連接配接向伺服器發送http請求,請求資料包。
6)伺服器處理HTTP請求,傳回響應。
7)浏覽器檢查HTTP響應是否為一個重定向(3XX結果狀态碼)、一個驗證請求(401)、錯誤(4XX、5XX)等等,這些都需要根據具體情況分類處理。PS:浏覽器對于常見HTTP狀态碼的反應
8)浏覽器接收HTTP響應并且可能關掉TCP連接配接,或者是重建立立連接配接使用新情求,獲得新響應。
9)浏覽器解碼響應,如果響應可以緩存,則存入緩存。
10)浏覽器顯示HTML頁面。
11)浏覽器發送請求擷取嵌入在HTML中的資源(html,css,javascript,圖檔,音樂······),對于未知類型,會彈出對話框。
12)浏覽器發送異步請求。
13)頁面全部渲染結束。
2、浏覽器解析和渲染過程

從圖中,我們可以看到,浏覽器解析大概分為四個流程,從解析html以建構dom樹 -> 建構render樹 -> 布局render樹 -> 繪制render樹。具體的流程如下:
(1)解析HTML建構DOM樹:渲染引擎開始解析HTML文檔,轉換樹中的HTML标簽或js生成的标簽到DOM節點,它被稱為---内容樹。
(2)建構渲染樹:解析css(外部css檔案和樣式元素以及js生成的樣式),根據css選擇器計算出節點的樣式,建立另一個樹---渲染樹。
(3)布局渲染樹:從根節點遞歸調用,計算每個元素的大小、位置等,給每個節點所應該出現在螢幕上的精确坐标。
(4)繪制渲染樹:周遊渲染樹。每個節點将使用UI後端來繪制。
值得注意的是,這個過程是逐漸完成的,為了更好的使用者體驗,渲染引擎将會盡可能早的将内容呈現到螢幕上,并不會等到所有的html都解析完成之後再去建構和布局render樹。它是解析完一部分内容就顯示一部分内容,同時,可能還在通過網絡下載下傳其餘内容。
3、什麼是 repaint 和 reflow
一個頁面由兩部分組成:
- DOM : 描述該頁面的結構
- render : 描述 DOM 節點 (nodes) 在頁面上如何呈現
當 DOM 元素的屬性發生變化 (如outline,visibility,background color時, 浏覽器會通知 render 重新描繪相應的元素, 此過程稱為 repaint。也就是說不涉及任何DOM元素的排版問題的變動為repaint,例如元素的color/text-align/text-decoration等等屬性的變動。
如果該次變化涉及元素布局 (如 width), 浏覽器則抛棄原有屬性, 重新計算并把結果傳遞給 render 以重新描繪頁面元素, 此過程稱為 reflow。也就是說除上面所提到的DOM元素style的修改基本為reflow。例如元素的任何涉及長、寬、行高、邊框、display等style的修改。
這兩個過程是很耗費浏覽器性能的, 從 IE 系列和 Chrome 渲染頁面速度上的差距即可看出渲染引擎計算對應值和呈現并不一定高效, 而每次對元素的操作都會發生 repaints 或 reflow, 是以編寫 DOM 互動時如果不注意就會導緻頁面性能低下。
但是,Reflow 的成本比 Repaint 的成本高得多的多。DOM Tree 裡的每個結點都會有 reflow 方法,一個結點的 reflow 很有可能導緻子結點,甚至父點以及同級結點的 reflow。在一些高性能的電腦上也許還沒什麼,但是如果 reflow 發生在手機上,那麼這個過程是非常痛苦和耗電的。
4、何時會發生repaint 和 reflow
1)觸發repaint
- color的修改,如color=#ddd;
- text-align的修改,如text-align=center;
- a:hover也會造成重繪。
- :hover引起的顔色等不導緻頁面回流的style變動。
- 等等太多
2)觸發reflow
- width/height/border/margin/padding的修改,如width=778px;
- 動畫,:hover等僞類引起的元素表現改動,display=none等造成頁面回流;
- appendChild等DOM元素操作;
- font類style的修改;
- background的修改,注意着字面上可能以為是重繪,但是浏覽器确實回流了,經過浏覽器廠家的優化,部分background的修改隻觸發repaint,當然IE不用考慮;
- scroll頁面,這個不可避免;
- resize頁面,桌面版本的進行浏覽器大小的縮放,移動端的話,還沒玩過能拖動程式,resize程式視窗大小的多視窗作業系統。
- 讀取元素的屬性(這個無法了解,但是技術達人是這麼說的,那就把它當做定理吧):讀取元素的某些屬性(offsetLeft、offsetTop、offsetHeight、offsetWidth、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()、currentStyle(in IE));
注:display:none 會觸發 reflow,而 visibility:hidden 隻會觸發 repaint,因為沒有發現位置變化。
5、如何優化
1)不要一條一條地修改 DOM 的樣式,盡量通過修改className來修改樣式 。
2)把 DOM 離線後修改。如:
- 使用 documentFragment 對象在記憶體裡操作 DOM。
- 先把 DOM 給 display:none (有一次 repaint),然後你想怎麼改就怎麼改。比如修改 100 次,然後再把他顯示出來。
- clone 一個 DOM 節點到記憶體裡,然後想怎麼改就怎麼改,改完後,和線上的那個的交換一下。
3)不要把 DOM 節點的屬性值放在一個循環裡當成循環裡的變量。不然這會導緻大量地讀寫這個結點的屬性。
4)盡可能的修改層級比較低的 DOM節點。當然,改變層級比較底的 DOM節點有可能會造成大面積的 reflow,但是也可能影響範圍很小。
5)為動畫的 HTML 元件使用 fixed 或 absoult 的 position,那麼修改他們的 CSS 是會大大減小 reflow 。
6)千萬不要使用 table 布局。因為可能很小的一個小改動會造成整個 table 的重新布局。
table元素一旦觸發reflow就會導緻table裡所有的其它元素 reflow。在适合用table的場合,可以設定table-layout為auto或fixed,這樣可以讓table一行一行的渲染,這種做法也是為了限制reflow的影響範圍。
7)盡可能少的修改元素style上的屬性 。
8)通過cssText屬性來設定樣式值。
9)緩存Layout屬性值 。
10)設定元素的position為absolute或fixed。
11)避免使用expression,他會每次調用都會重新計算一遍(包括加載頁面)。