寫在前面
在討論今天的主角之前谬莹,我們要先了解一下瀏覽器的渲染機(jī)制。以Google
,Firefox
,Safari
為例,Firefox
使用Geoko——Mozilla
自主研發(fā)的渲染引擎附帽,Safari
和Chrome
都使用 webkit
埠戳。
我們主要以 Webkit
的主流程為例
- 瀏覽器使用流式布局模型 (Flow Based Layout)
- 解析
HTML
生成DOM
樹 - 解析
CSS
生成CSSOM
規(guī)則樹 - 將
DOM
樹與CSSOM
規(guī)則樹合并在一起生成渲染樹Render Tree
- 根據(jù)渲染樹遍歷拿到每個節(jié)點(diǎn)開始布局,計(jì)算每個節(jié)點(diǎn)的位置大小信息
- 將渲染樹每個節(jié)點(diǎn)繪制到屏幕
回流(Reflow)
上面我們知道蕉扮,我們會根據(jù) Render Tree
去遍歷渲染整胃,所以當(dāng)我們的節(jié)點(diǎn)發(fā)生改變時(shí),瀏覽器重新渲染部分節(jié)點(diǎn)或者全部文檔喳钟,我們稱這個過程為回流
大致整理會導(dǎo)致回流的一些操作
- 頁面首次渲染
- 瀏覽器窗口大小發(fā)生改變
- 元素尺寸或位置發(fā)生改變
- 元素內(nèi)容變化(文字?jǐn)?shù)量或圖片大小等等)
- 元素字體大小變化
- 添加或者刪除可見的DOM元素
- 激活CSS偽類(例如::hover)
- 查詢某些屬性或調(diào)用某些方法
主要有下面幾個API
盒子操作相關(guān)
-
elem.offsetLeft
,elem.offsetTop
,elem.offsetWidth
,elem.offsetHeight
,elem.offsetParent
-
elem.clientLeft
,elem.clientTop
,elem.clientWidth
,elem.clientHeight
-
elem.getClientRects()
,elem.getBoundingClientRect()
滾動相關(guān)
-
elem.scrollBy()
,elem.scrollTo()
-
elem.scrollIntoView()
,elem.scrollIntoViewIfNeeded()
-
elem.scrollWidth
,elem.scrollHeight
-
elem.scrollLeft
,elem.scrollTop
其他
上述主要是我們經(jīng)常使用的一些API,其他還有一個api
已經(jīng)有熱心網(wǎng)友幫我們整理出來了
我們可以看一下
What forces layout / reflow
重繪(Repaint)
當(dāng)我們操作的節(jié)點(diǎn)上的元素并不導(dǎo)致元素位置發(fā)生變化時(shí)屁使,比如color
,background-color
,visibility(注意雖然節(jié)點(diǎn)隱藏了,但是元素還在荚藻,并且位置也不會發(fā)生變化)
瀏覽器會將新的樣式賦值給這些節(jié)點(diǎn)屋灌,我們稱這個過程為重繪
影響
按照常理也很好理解,因?yàn)槲恢糜τ笮〉劝l(fā)生的回流操作相比于僅僅是顏色的變化共郭,帶給我們的視覺直觀感受來說,回流是比較大的疾呻。
事實(shí)上除嘹,回流確實(shí)比重繪的成本更大,并且有時(shí)候并不是只回流一個元素岸蜗,甚至?xí)痈冈鼗蛘咦釉匾黄鸹亓鳌?/p>
現(xiàn)代瀏覽器會對頻繁的回流或重繪操作進(jìn)行優(yōu)化尉咕,瀏覽器會維護(hù)一個隊(duì)列,當(dāng)我們頁面發(fā)生回流或重繪時(shí)璃岳,有時(shí)候并不是立即執(zhí)行年缎,而是先放入維護(hù)的隊(duì)列中,到達(dá)一定時(shí)間后統(tǒng)一去進(jìn)行繪制
當(dāng)你訪問以下屬性或方法時(shí)铃慷,瀏覽器會立刻清空隊(duì)列
clientWidth
单芜、clientHeight
、clientTop
犁柜、clientLeft
offsetWidth
洲鸠、offsetHeight
、offsetTop馋缅、
offsetLeft`
scrollWidth
扒腕、scrollHeight
、scrollTop
萤悴、scrollLeft
width
瘾腰、height
getComputedStyle()
getBoundingClientRect()
所以當(dāng)我們需要使用如上api
獲取數(shù)據(jù)時(shí),我們要注重渲染時(shí)機(jī)以及取值時(shí)機(jī)
避免重繪與回流
- 避免使用
table
布局稚疹。 - 盡可能在
DOM
樹的最末端改變class
居灯。 - 避免設(shè)置多層內(nèi)聯(lián)樣式祭务。
- 將動畫效果應(yīng)用到
position
屬性為absolute
或fixed
的元素上。 - 避免使用
CSS
表達(dá)式(例如:calc()
)怪嫌。 - 避免頻繁操作樣式义锥,最好一次性重寫
style
屬性,或者將樣式列表定義為class
并一次性更改class
屬性岩灭。
-避免頻繁操作DOM
拌倍,創(chuàng)建一個documentFragment
,在它上面應(yīng)用所有DOM
操作噪径,最后再把它添加到文檔中柱恤。 - 也可以先為元素設(shè)置
display: none
,操作結(jié)束后再把它顯示出來找爱。因?yàn)樵?code>display屬性為none
的元素上進(jìn)行的DOM
操作不會引發(fā)回流和重繪梗顺。 - 避免頻繁讀取會引發(fā)回流/重繪的屬性,如果確實(shí)需要多次使用车摄,就用一個變量緩存起來寺谤。
- 對具有復(fù)雜動畫的元素使用絕對定位,使它脫離文檔流吮播,否則會引起父元素及后續(xù)元素頻繁回流变屁。
總結(jié)
我們把頁面文檔比作一個積木的話,我們抽離中間或者底部的一個積木塊意狠,我們的積木會重新找到重心并且穩(wěn)固下來粟关,我們把這個過程稱之為回流
我們在某個積木上涂上顏色,這并不會造成整個積木的穩(wěn)定环戈,我們把這個過程叫做重繪
或者說闷板,我們簡單理解會引起元素位置變化的就會reflow
,會引起位置變化的,只是在以前的位置進(jìn)行改變背景顏色等院塞,只會repaint
本文首發(fā)于什么是回流與重繪 (Reflow & Repaint)