第一次聽到重繪和回流是在鵝廠實習面試,那個時候對瀏覽器沒有任何的概念,所以面試官說到這個問題的時候整個人都蒙圈了。下面是近期學習總結:
重繪(repaint)和回流(reflow)
文檔初次加載時,HTML解析器會解析元素構建DOM樹,CSS解析器將樣式解析為樣式結構體,之后通過DOM樹和樣式結構體構建渲染樹,渲染樹具有樣式屬性。然后遍歷渲染樹的每個渲染器將器布局到頁面,最后瀏覽器將器繪制到頁面。
重繪:當元素的外觀或外觀可見性(visibility)發生變化時會觸發重繪
回流:render樹中的部分或全部因為元素的規模尺寸、布局、隱藏等改變,需要重新計算render樹。
回流時,瀏覽器會使渲染樹中受影響的部分失效,然后重新構造這部分的render樹。完成回流之后瀏覽器會重新布局、繪制受影響的部分到屏幕中,該過程就是重繪。所以回流必定會引起重繪,但重繪不一定引起回流。
每個頁面至少需要一次回流,就是頁面第一次加載時。回流變化涉及到部分頁面(或整個頁面)的布局。一個元素的回流導致其所有子元素以及DOM中緊隨其后的祖先元素和其子元素都發生回流。
是什么導致回流呢?
(1) 調整窗口大小----發生resize事件時
(2) 元素位置改變
(3) 元素尺寸改變——邊距、填充、邊框、高度和寬度
(4) 內容改變——比如文本字體或圖片大小改變而引起的寬度和高度改變
(5) 頁面渲染初始化
(6) 添加或刪除可見的DOM元素
聰明的瀏覽器
很多瀏覽器都會優化repaint和reflow操作,瀏覽器會維護1個隊列,把所有會引起回流、重繪的操作放入這個隊列,等隊列中的操作到了一定的數量或者到了一定的時間間隔,瀏覽器就會把flush隊列,進行一個批處理。這樣就會讓多次的回流、重繪變成一次回流重繪。 雖然有了瀏覽器的優化,但有時候我們寫的一些代碼可能會強制瀏覽器提前flush隊列,比如設置width,offsetTop,scrollTop,clientTop精確值時,這樣瀏覽器的優化可能就起不到作用了。
避免回流和重繪方法
減少回流、重繪其實就是需要減少對render tree的操作,並減少對一些style信息的請求,盡量利用好瀏覽器的優化策略
(1) 避免操作DOM,創建一個documentFragment或div,在它上面應用所有DOM操作,最后再把它添加到window.document。也可以在一個display:none的元素上進行操作,最終把它顯示出來。因為display:none上的DOM操作不會引發回流和重繪。
(2) 讓要操作的元素進行"離線處理",處理完后一起更新,這里所謂的"離線處理"即讓元素不存在於render tree中。如讀取offsetLeft等屬性。
(3) 盡可能在DOM樹的末端改變class ,盡可能在DOM樹的里面改變class,可以限制回流的范圍,使其影響盡可能少的節點。
(4) 避免設置多層內聯樣式,因為每一個都會造成回流,樣式合並在一個外部類,這樣當該元素的class屬性被操作時,只會產生一個reflow。
(5) 將需要多次回流的元素position屬性設為absolute或fixed,這樣該元素就會脫離文檔流,它的變化不會影響其他元素變化。比如動畫效果應用到position屬性為absolute或fixed的元素上。
(6) 犧牲平滑度換取速度,動畫元素每次移動3像素可能在非常快的機器上看起來平滑度低了,但它不會導致CPU在較慢的機器和移動設備中抖動
(7) 避免使用table布局,在布局完全建立之前,table需要很多關口,table是可以影響之前已經進入的DOM元素的顯示的元素。即使一些小的變化和會導致table中所有其他節點回流。
(8) 避免使用css的JavaScript表達式,該規則較過時,但是個好主意。因為每次都需要重新計算文檔,或部分文檔、回流。