https://juejin.im/post/5a9923e9518825558251c96a
瀏覽器使用流式布局模型 (Flow Based Layout)。
瀏覽器會把HTML解析成DOM,把CSS解析成CSSOM,DOM和CSSOM合并就產(chǎn)生了Render Tree。
有了RenderTree,我們就知道了所有節(jié)點的樣式,然后計算他們在頁面上的大小和位置,最后把節(jié)點繪制到頁面上。
由于瀏覽器使用流式布局,對Render Tree的計算通常只需要遍歷一次就可以完成,但table及其內(nèi)部元素除外,他們可能需要多次計算,通常要花3倍于同等元素的時間,這也是為什么要避免使用table布局的原因之一。
一句話:回流必將引起重繪,重繪不一定會引起回流。
回流 (Reflow)
當(dāng)Render Tree中部分或全部元素的尺寸、結(jié)構(gòu)、或某些屬性發(fā)生改變時,瀏覽器重新渲染部分或全部文檔的過程稱為回流。
會導(dǎo)致回流的操作:
頁面首次渲染
瀏覽器窗口大小發(fā)生改變
元素尺寸或位置發(fā)生改變
元素內(nèi)容變化(文字?jǐn)?shù)量或圖片大小等等)
元素字體大小變化
添加或者刪除可見的DOM元素
激活CSS偽類(例如::hover)
查詢某些屬性或調(diào)用某些方法
一些常用且會導(dǎo)致回流的屬性和方法:
clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、offsetHeight、offsetTop、offsetLeft
scrollWidth、scrollHeight、scrollTop、scrollLeft
scrollIntoView()、scrollIntoViewIfNeeded()
getComputedStyle()
getBoundingClientRect()
scrollTo()
重繪 (Repaint)
當(dāng)頁面中元素樣式的改變并不影響它在文檔流中的位置時(例如:color、background-color、visibility等),瀏覽器會將新樣式賦予給元素并重新繪制它,這個過程稱為重繪。
性能影響
回流比重繪的代價要更高。
有時即使僅僅回流一個單一的元素,它的父元素以及任何跟隨它的元素也會產(chǎn)生回流。
現(xiàn)代瀏覽器會對頻繁的回流或重繪操作進(jìn)行優(yōu)化:
瀏覽器會維護(hù)一個隊列,把所有引起回流和重繪的操作放入隊列中,如果隊列中的任務(wù)數(shù)量或者時間間隔達(dá)到一個閾值的,瀏覽器就會將隊列清空,進(jìn)行一次批處理,這樣可以把多次回流和重繪變成一次。
當(dāng)你訪問以下屬性或方法時,瀏覽器會立刻清空隊列:
clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、offsetHeight、offsetTop、offsetLeft
scrollWidth、scrollHeight、scrollTop、scrollLeft
width、height
getComputedStyle()
getBoundingClientRect()
因為隊列中可能會有影響到這些屬性或方法返回值的操作,即使你希望獲取的信息與隊列中操作引發(fā)的改變無關(guān),瀏覽器也會強(qiáng)行清空隊列,確保你拿到的值是最精確的。
如何避免
CSS
避免使用table布局。
盡可能在DOM樹的最末端改變class。
避免設(shè)置多層內(nèi)聯(lián)樣式。
將動畫效果應(yīng)用到position屬性為absolute或fixed的元素上。
避免使用CSS表達(dá)式(例如:calc())。
JavaScript
避免頻繁操作樣式,最好一次性重寫style屬性,或者將樣式列表定義為class并一次性更改class屬性。
避免頻繁操作DOM,創(chuàng)建一個documentFragment,在它上面應(yīng)用所有DOM操作,最后再把它添加到文檔中。
也可以先為元素設(shè)置display: none,操作結(jié)束后再把它顯示出來。因為在display屬性為none的元素上進(jìn)行的DOM操作不會引發(fā)回流和重繪。
避免頻繁讀取會引發(fā)回流/重繪的屬性,如果確實需要多次使用,就用一個變量緩存起來。
對具有復(fù)雜動畫的元素使用絕對定位,使它脫離文檔流,否則會引起父元素及后續(xù)元素頻繁回流。