1.什麼是重繪(replaint)和回流(relfow)
reflow:當render樹中的一部分或者全部因為大小邊距等問題發生改變而需要重建的過程。
repaint:當元素的一部分屬性發生變化,如外觀背景色不會引起布局變化而需要重新渲染的過程叫做重繪。
2.什麼會引起回流
籠統來說當頁面的布局和幾何屬性發生變化的時候就會引起回流,具體來說可以分為5類:
1.dom樹結構發生變化,比如說删除或者添加某一個node節點。
2.元素幾何屬性發生變化,包括padding、margin、height、width、border等。
3.頁面渲染初始化。
4.擷取某些屬性。雖然浏覽器引擎可能會針對重排做了優化,比如說opera,他會等到有足夠的變化發生,或者等到一定時間,或者等一個線程結束,再一起處理,這樣就隻重繪一次,但是除了render數的直接變化,當擷取一些屬性時,浏覽器為取得正确的值也會觸發回流,這樣使得浏覽器的優化失效了。這些屬性包括:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、 clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle() (currentStyle in IE)。是以,在多次使用這些值時應進行緩存。(這段我是直接引用的。。。)
5.浏覽器視窗發生變化-resize事件發生時
現在我們大概都能得出的結論是:回流比重繪的代價要高,至于具體的花銷跟render樹有多少節點需要重新建構有關。
還有就是,回流一定會伴随着重繪,但是重繪不一定會引起回流。
3.我們要如何避免
上面的執行個體代碼中可以看到一共七行代碼引起了6次左右的回流、重繪(上面的代碼我大老遠從别的頁面拿過來當然不隻是就用那一次,哈哈),而且我們剛剛還知道了回流花銷真是不小,那麼浏覽器是不是真的每次js語句引起了回流他就執行一下呢?恩,後面的内容我是看的參考資料的:等隊列中的操作到了一定的數量或者到了一定的時間間隔,浏覽器就會flush隊列,進行一個批處理。這樣就會讓多次的回流、重繪變成一次回流重繪。
但是盡管浏覽器挺機智地幫我們優化了代碼,我們自己作死也是沒救的,比如你去請求
1. offsetTop, offsetLeft, offsetWidth, offsetHeight
2. scrollTop/Left/Width/Height
3. clientTop/Left/Width/Height
4. width,height
5. 請求了getComputedStyle(), 或者 IE的 currentStyle
浏覽器為了給你傳回一個比較精确的答案,他會提前flush隊列,因為隊列中可能會有影響這些值的操作。
是以我們可以做的是:
1.将那些改變樣式的操作集合在一次完事,直接改變className或者cssText
2.讓要操作的元素進行離線處理,處理完事以後再一起更新
(1)使用DocumentFragment進行緩存操作,引發一次回流和重繪
課外延伸:
DocumentFragment 節點不屬于文檔樹,繼承的 parentNode 屬性總是 null。
不過它有一種特殊的行為,該行為使得它非常有用,即當請求把一個 DocumentFragment 節點插入文檔樹時,插入的不是 DocumentFragment 自身,而是它的所有子孫節點。這使得 DocumentFragment 成了有用的占位符,暫時存放那些一次插入文檔的節點。它還有利于實作文檔的剪切、複制和粘貼操作。
其實他就是一個遊離在DOM樹外面的容器,是以你在把它插入文檔節點之前,随便給他增删節點都不會引起回流
(2)使用display:none,隻引發兩次回流和重繪。道理跟上面的一樣。因為display:none的元素不會出現在render樹
(3)使用cloneNode和replaceChild技術,引發一次回流和重繪(這條其實沒太明白)
3.不要經常通路會引起浏覽器flush隊列的屬性,非要高頻通路的話建議緩存到變量;
4.将需要多次重排的元素,position屬性設為absolute或fixed,這樣此元素就脫離了文檔流,它的變化不會影響到其他元素。例如有動畫效果的元素就最好設定為絕對定位;
5.盡量不要使用表格布局,如果沒有定寬表格一列的寬度由最寬的一列決定,那麼很可能在最後一行的寬度超出之前的列寬,引起整體回流造成table可能需要多次計算才能确定好其在渲染樹中節點的屬性,通常要花3倍于同等元素的時間。