回流必将引起重繪,重繪不一定會引起回流
在讨論回流與重繪之前,我們要知道:
- 浏覽器使用流式布局模型 (Flow Based Layout)。
- 浏覽器會把HTML解析成DOM,把CSS解析成CSSOM,DOM和CSSOM合并就産生了Render Tree。
- 有了RenderTree,我們就知道了所有節點的樣式,然後計算他們在頁面上的大小和位置,最後把節點繪制到>頁面上。
- 由于浏覽器使用流式布局,對Render Tree的計算通常隻需要周遊一次就可以完成,但table及其内部元素除>外,他們可能需要多次計算,通常要花3倍于同等元素的時間,這也是為什麼要避免使用table布局的原因之一。
回流 (Reflow)
當Render Tree中部分或全部元素的尺寸、結構、或某些屬性發生改變時,浏覽器重新渲染部分或全部文檔的過程稱為回流。
會導緻回流的操作:
頁面首次渲染
浏覽器視窗大小發生改變
元素尺寸或位置發生改變
元素内容變化(文字數量或圖檔大小等等)
元素字型大小變化
添加或者删除可見的DOM元素
激活CSS僞類(例如::hover)
查詢某些屬性或調用某些方法
一些常用且會導緻回流的屬性和方法:
clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、offsetHeight、offsetTop、offsetLeft
scrollWidth、scrollHeight、scrollTop、scrollLeft
scrollIntoView()、scrollIntoViewIfNeeded()
getComputedStyle()
getBoundingClientRect()
scrollTo()
重繪 (Repaint)
當頁面中元素樣式的改變并不影響它在文檔流中的位置時(例如:color、background-color、visibility等),浏覽器會将新樣式賦予給元素并重新繪制它,這個過程稱為重繪。
性能影響
回流比重繪的代價要更高
有時即使僅僅回流一個單一的元素,它的父元素以及任何跟随它的元素也會産生回流。
現代浏覽器會對頻繁的回流或重繪操作進行優化:
浏覽器會維護一個隊列,把所有引起回流和重繪的操作放入隊列中,如果隊列中的任務數量或者時間間隔達到一個門檻值的,浏覽器就會将隊列清空,進行一次批處理,這樣可以把多次回流和重繪變成一次。
當你通路以下屬性或方法時,浏覽器會立刻清空隊列:
clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、offsetHeight、offsetTop、offsetLeft
scrollWidth、scrollHeight、scrollTop、scrollLeft
width、height
getComputedStyle()
getBoundingClientRect()
因為隊列中可能會有影響到這些屬性或方法傳回值的操作,即使你希望擷取的資訊與隊列中操作引發的改變無關,浏覽器也會強行清空隊列,確定你拿到的值是最精确的。
如何避免
CSS
避免使用table布局。
盡可能在DOM樹的最末端改變class。
避免設定多層内聯樣式。
将動畫效果應用到position屬性為absolute或fixed的元素上。
避免使用CSS表達式(例如:calc())。
JavaScript
避免頻繁操作樣式,最好一次性重寫style屬性,或者将樣式清單定義為class并一次性更改class屬性。
避免頻繁操作DOM,建立一個documentFragment,在它上面應用所有DOM操作,最後再把它添加到文檔中。
也可以先為元素設定display: none,操作結束後再把它顯示出來。因為在display屬性為none的元素上進行的DOM操作不會引發回流和重繪。
避免頻繁讀取會引發回流/重繪的屬性,如果确實需要多次使用,就用一個變量緩存起來。
對具有複雜動畫的元素使用絕對定位,使它脫離文檔流,否則會引起父元素及後續元素頻繁回流。