重流(Reflow)是指布局引擎為frame計算圖形的過程。 frame是一個矩形,擁有寬高和相對父容器的偏移。frame用來顯示盒模型(content model), 但一個content model可能會顯示為多個frame,比如換行的文本每行都會顯示為一個frame。
重繪(Repaint)發生在元素的可見性發生變化時,比如背景色、前景色等。 因此回流必然會引起重繪。
重流的原因:
- 初始化(Initial)。DOM載入后的第一次回流,將會遍歷所有frame。
- 漸進(Incremental)。當一個frame發生漸進回流時,意味着它前面的元素都沒有變, 而是它里面的元素變了。這會引起自底向上的作用。
- 改變大小(Resize)。元素的容器邊界發生變化時,此時元素內部狀態沒變。 在計算自頂向下的布局約束的同時,可以復用內部狀態。
- 樣式改變(StyleChange)。整個frame樹都應得到遍歷。
- Dirty。當一個容器已經緩存了多個子元素的Incremental回流時,該容器出於Dirty的狀態。
如果你是Web開發者,可能更關注的是哪些具體原因會引起瀏覽器的回流,下面羅列一下:
- 調整窗口大小
- 改變字體大小
- 樣式表變動
- 元素內容變化,尤其是輸入控件
- CSS偽類激活
- DOM操作
- offsetWidth, width, clientWidth, scrollTop/scrollHeight的計算, 會使瀏覽器將漸進回流隊列Flush,立即執行回流。
最佳實踐
對我們Web開發者最有用的還是如何去做,才能減少頁面回流。先來個例子:
var s = document.body.style; s.padding = "2px"; // 回流+重繪 s.border = "1px solid red"; // 再一次 回流+重繪 s.color = "blue"; // 再一次重繪 s.backgroundColor = "#ccc"; // 再一次 重繪 s.fontSize = "14px"; // 再一次 回流+重繪 // 添加node,再一次 回流+重繪 document.body.appendChild(document.createTextNode('abc!'));
可以看到每次DOM元素的樣式操作都會引發重繪,如果涉及布局還會引發回流。
避免大量頁面回流的手段也有很多,其本質都是盡量減少引起回流和重繪的DOM操作:
- 避免逐項更改樣式。最好一次性更改style屬性,或者將樣式列表定義為class並一次性更改class屬性。
- 避免循環操作DOM。創建一個documentFragment或div,在它上面應用所有DOM操作,最后再把它添加到window.document。
- 避免循環讀取offsetLeft等屬性。在循環之前把它們存起來。
- 絕對定位具有復雜動畫的元素。絕對定位使它脫離文檔劉,否則會引起父元素及后續元素大量的回流。
