介紹下重繪和回流(Repaint & & Reflow ),以及如何進(jìn)行優(yōu)化
參考文檔: https://mp.weixin.qq.com/s/_NrFbrucJRrA8fS40dCkow
- 瀏覽器渲染機(jī)制
瀏覽器采用流式布局模型(Flow Based Layout)
瀏覽器會(huì)把 HTML 解析成 DOM,把 CSS 解析成 CSSOM,DOM 和 CSSOM 合并就
產(chǎn)生了渲染樹(Render Tree)。
有了 RenderTree,我們就知道了所有節(jié)點(diǎn)的樣式,然后計(jì)算他們?cè)陧撁嫔系拇?br> 小和位置,最后把節(jié)點(diǎn)繪制到頁面上。
由于瀏覽器使用流式布局,對(duì) Render Tree 的計(jì)算通常只需要遍歷一次就可以完
成,但 table 及其內(nèi)部元素除外,他們可能需要多次計(jì)算,通常要花 3 倍于同
等元素的時(shí)間,這也是為什么要避免使用 table 布局的原因之一。
- 瀏覽器渲染機(jī)制
- 重繪
由于節(jié)點(diǎn)的幾何屬性發(fā)生改變或者由于樣式發(fā)生改變而不會(huì)影響布局的,稱為
重繪,例如 outline, visibility, color、background-color 等,重繪的代價(jià)是高昂的,
因?yàn)闉g覽器必須驗(yàn)證 DOM 樹上其他節(jié)點(diǎn)元素的可見性。
- 重繪
- 回流
回流是布局或者幾何屬性需要改變就稱為回流?;亓魇怯绊憺g覽器性能的關(guān)鍵
因素,因?yàn)槠渥兓婕暗讲糠猪撁妫ɑ蚴钦麄€(gè)頁面)的布局更新。一個(gè)元素的
回流可能會(huì)導(dǎo)致了其所有子元素以及 DOM 中緊隨其后的節(jié)點(diǎn)、祖先節(jié)點(diǎn)元素
的隨后的回流。
- 回流
<body>
<div class="error">
<h4>我的組件</h4>
<p><strong>錯(cuò)誤:</strong>錯(cuò)誤的描述…</p>
<h5>錯(cuò)誤糾正</h5>
<ol>
<li>第一步</li>
<li>第二步</li>
</ol>
</div>
</body>
在上面的 HTML 片段中,對(duì)該段落(<p>標(biāo)簽)回流將會(huì)引發(fā)強(qiáng)烈的回流,因?yàn)樗?br>
是一個(gè)子節(jié)點(diǎn)。這也導(dǎo)致了祖先的回流(div.error 和 body – 視瀏覽器而定)。
此外,<h5>和<ol>也會(huì)有簡(jiǎn)單的回流,因?yàn)槠湓?DOM 中在回流元素之后。大部
分的回流將導(dǎo)致頁面的重新渲染。
回流必定會(huì)發(fā)生重繪,重繪不一定會(huì)引發(fā)回流。
- 瀏覽器優(yōu)化
現(xiàn)代瀏覽器大多都是通過隊(duì)列機(jī)制來批量更新布局,瀏覽器會(huì)把修改操作放在
隊(duì)列中,至少一個(gè)瀏覽器刷新(即 16.6ms)才會(huì)清空隊(duì)列,但當(dāng)你獲取布局信
息的時(shí)候,隊(duì)列中可能有會(huì)影響這些屬性或方法返回值的操作,即使沒有,瀏
覽器也會(huì)強(qiáng)制清空隊(duì)列,觸發(fā)回流與重繪來確保返回正確的值。
主要包括以下屬性或方法:(實(shí)時(shí)計(jì)算屬性)
1、offsetTop、offsetLeft、offsetWidth、offsetHeight
2、scrollTop、scrollLeft、scrollWidth、scrollHeight
3、clientTop、clientLeft、clientWidth、clientHeight
4、width、height
5、getComputedStyle()
6、getBoundingClientRect()
所以,我們應(yīng)該避免頻繁的使用上述的屬性,他們都會(huì)強(qiáng)制渲染刷新隊(duì)列
- 瀏覽器優(yōu)化
- 減少重繪與回流
CSS
1、使用 transform 替代 top
2、使用 visibility 替換 display: none ,因?yàn)榍罢咧粫?huì)引起重繪,后者會(huì)引發(fā)回
流(改變了布局)
3、避免使用 table 布局,可能很小的一個(gè)小改動(dòng)會(huì)造成整個(gè) table 的重新布局。
4、盡可能在 DOM 樹的最末端改變 class,回流是不可避免的,但可以減少其影
響。盡可能在 DOM 樹的最末端改變 class,可以限制了回流的范圍,使其影響
盡可能少的節(jié)點(diǎn)。
5、避免設(shè)置多層內(nèi)聯(lián)樣式,CSS 選擇符從右往左匹配查找,避免節(jié)點(diǎn)層級(jí)過多
<div>
<a>
<span></span>
</a>
</div>
<style>
span {
color: red;
}
div > a > span {
color: red;
}
</style>
對(duì)于第一種設(shè)置樣式的方式來說,瀏覽器只需要找到頁面中所有的 span 標(biāo)簽
然后設(shè)置顏色,但是對(duì)于第二種設(shè)置樣式的方式來說,瀏覽器首先需要找到所
有的 span 標(biāo)簽,然后找到 span 標(biāo)簽上的 a 標(biāo)簽,最后再去找到 div 標(biāo)簽,
然后給符合這種條件的 span 標(biāo)簽設(shè)置顏色,這樣的遞歸過程就很復(fù)雜。所以
我們應(yīng)該盡可能的避免寫過于具體的 CSS 選擇器,然后對(duì)于 HTML 來說也盡
量少的添加無意義標(biāo)簽,保證層級(jí)扁平。
將動(dòng)畫效果應(yīng)用到 position 屬性為 absolute 或 fixed 的元素上,避免影響其他元
素的布局,這樣只是一個(gè)重繪,而不是回流,同時(shí),控制動(dòng)畫速度可以選擇
requestAnimationFrame,詳見探討 requestAnimationFrame。
避免使用 CSS 表達(dá)式,可能會(huì)引發(fā)回流。
將頻繁重繪或者回流的節(jié)點(diǎn)設(shè)置為圖層,圖層能夠阻止該節(jié)點(diǎn)的渲染行為影響
別的節(jié)點(diǎn),例如 will-change、video、iframe 等標(biāo)簽,瀏覽器會(huì)自動(dòng)將該節(jié)點(diǎn)變
為圖層。
CSS3 硬件加速(GPU 加速),使用 css3 硬件加速,可以讓 transform、opacity、
filters 這些動(dòng)畫不會(huì)引起回流重繪 。但是對(duì)于動(dòng)畫的其它屬性,比如
background-color 這些,還是會(huì)引起回流重繪的,不過它還是可以提升這些動(dòng)畫
的性能。
DOM離線(display:none -> ,,,,,, -> display:inline-block)
JavaScript
1、避免頻繁操作樣式,最好一次性重寫 style 屬性,或者將樣式列表定義為 class
并一次性更改 class 屬性。
2、避免頻繁操作 DOM,創(chuàng)建一個(gè) documentFragment,在它上面應(yīng)用所有 DOM
操作,最后再把它添加到文檔中。
3、避免頻繁讀取會(huì)引發(fā)回流/重繪的屬性,如果確實(shí)需要多次使用,就用一個(gè)
變量緩存起來。
4、對(duì)具有復(fù)雜動(dòng)畫的元素使用絕對(duì)定位,使它脫離文檔流,否則會(huì)引起父元素
及后續(xù)元素頻繁回流。