JS基礎(chǔ)知識儲備(回流與重繪及優(yōu)化策略)

1、瀏覽器渲染過程

image

從上面這個圖上,我們可以看到,瀏覽器渲染過程如下:

  • 解析HTML,生成DOM樹,解析CSS,生成CSSOM樹
  • 將DOM樹和CSSOM樹結(jié)合,生成渲染樹(Render Tree)
  • Layout(回流):根據(jù)生成的渲染樹,進(jìn)行回流(Layout),得到節(jié)點(diǎn)的幾何信息(位置,大?。?/li>
  • Painting(重繪):根據(jù)渲染樹以及回流得到的幾何信息,得到節(jié)點(diǎn)的絕對像素
  • Display:將像素發(fā)送給GPU,展示在頁面上。(這一步其實(shí)還有很多內(nèi)容,比如會在GPU將多個合成層合并為同一個層,并展示在頁面中。而css3硬件加速的原理則是新建合成層,這里我們不展開,之后有機(jī)會會寫一篇博客)

渲染過程看起來很簡單,讓我們來具體了解下每一步具體做了什么。

生成渲染樹

image

為了構(gòu)建渲染樹,瀏覽器主要完成了以下工作:

  1. 從DOM樹的根節(jié)點(diǎn)開始遍歷每個可見節(jié)點(diǎn)。
  2. 對于每個可見的節(jié)點(diǎn),找到CSSOM樹中對應(yīng)的規(guī)則,并應(yīng)用它們。
  3. 根據(jù)每個可見節(jié)點(diǎn)以及其對應(yīng)的樣式,組合生成渲染樹。

第一步中,既然說到了要遍歷可見的節(jié)點(diǎn),那么我們得先知道,什么節(jié)點(diǎn)是不可見的。不可見的節(jié)點(diǎn)包括:

  • 一些不會渲染輸出的節(jié)點(diǎn),比如script、meta、link等。
  • 一些通過css進(jìn)行隱藏的節(jié)點(diǎn)。比如display:none。注意,利用visibility和opacity隱藏的節(jié)點(diǎn),還是會顯示在渲染樹上的。只有display:none的節(jié)點(diǎn)才不會顯示在渲染樹上。

注意:渲染樹只包含可見的節(jié)點(diǎn)

2、回流與重繪

1、概述

  • 回流:當(dāng)render tree中的一部分(或全部),因?yàn)樵氐囊?guī)模尺寸、布局、隱藏等改變而需要重新構(gòu)建,這就是回流(reflow)
  • 重繪:回流完成后,瀏覽器會重新繪制受影響的部分,這就是重繪過程。
    當(dāng)render tree中的一些元素需要更新屬性,而這些屬性只是影響元素的外觀、風(fēng)格,而不影響布局,則稱為重繪(repaints)

2、何時發(fā)生回流重繪

當(dāng)頁面布局和幾何屬性改變時就需要回流

  • 添加或刪除可見的DOM元素
  • 元素的位置發(fā)生變化(offsetWidth、offsetHeight)
  • 元素尺寸發(fā)生變化(包括外邊距、內(nèi)邊框、邊框大小、高度和寬度等)
  • 內(nèi)容發(fā)生變化(文本、圖片、input框)
  • 頁面渲染初始化
  • 瀏覽器窗口尺寸改變(回流是根據(jù)視口的大小來計(jì)算元素的位置和大小的)
  • 增加或移除樣式表
  • 操作class屬性
  • 改變字體
  • 激活偽類(如:hover)

注意:回流一定會觸發(fā)重繪,而重繪不一定會回流
(比如在body最前面插入一個元素,會導(dǎo)致整個render tree回流,如果在body后面插入一個元素,則不會影響前面元素的回流)

3、瀏覽器的優(yōu)化機(jī)制

現(xiàn)代的瀏覽器都是很聰明的,由于每次重排都會造成額外的計(jì)算消耗,因此大多數(shù)瀏覽器都會通過隊(duì)列化修改并批量執(zhí)行來優(yōu)化重排過程。瀏覽器會將修改操作放入到隊(duì)列里,直到過了一段時間或者操作達(dá)到了一個閾值,才清空隊(duì)列。但是!當(dāng)你獲取布局信息的操作的時候,會強(qiáng)制隊(duì)列刷新,比如當(dāng)你訪問以下屬性或者使用以下方法:

  • offsetTop、offsetLeft、offsetWidth、offsetHeight
  • scrollTop、scrollLeft、scrollWidth、scrollHeight
  • clientTop、clientLeft、clientWidth、clientHeight
  • getComputedStyle() 或者 IE中:currentStyle
  • getBoundingClientRect
  • 具體可以訪問這個網(wǎng)站:https://gist.github.com/pauli...

以上屬性和方法都需要返回最新的布局信息,因此瀏覽器不得不清空隊(duì)列,觸發(fā)回流重繪來返回正確的值。因此,我們在修改樣式的時候,最好避免使用上面列出的屬性,他們都會刷新渲染隊(duì)列。如果要使用它們,最好將值緩存起來。

3、如何減少回流重繪

1、最小化重繪

減少對render tree的操作,并減少一些對style信息的請求,合理利用瀏覽器的優(yōu)化策略

const el = document.getElementById('test');
el.style.padding = '5px';
el.style.borderLeft = '1px';
el.style.borderRight = '2px';
  • 使用cssText
const el = document.getElementById('test');
el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px;';
  • 修改CSS的classname
const el = document.getElementById('test');
el.className += ' active';

2、批量處理DOM

當(dāng)我們需要對DOM對一系列修改的時候,可以通過以下步驟減少回流重繪次數(shù):

  1. 使元素脫離文檔流
  2. 對其進(jìn)行多次修改
  3. 將元素帶回到文檔中。

該過程的第一步和第三步可能會引起回流,但是經(jīng)過第一步之后,對DOM的所有修改都不會引起回流,因?yàn)樗呀?jīng)不在渲染樹了。

有三種方式可以讓DOM脫離文檔流:

  • 隱藏元素,應(yīng)用修改,重新顯示
  • 使用文檔片段(document fragment)在當(dāng)前DOM之外構(gòu)建一個子樹,再把它拷貝回文檔。
  • 將原始元素拷貝到一個脫離文檔的節(jié)點(diǎn)中,修改節(jié)點(diǎn)后,再替換原始的元素。

3、避免觸發(fā)同步布局事件

當(dāng)我們訪問元素的一些屬性的時候,會導(dǎo)致瀏覽器強(qiáng)制清空隊(duì)列,進(jìn)行強(qiáng)制同步布局。舉個例子,比如說我們想將一個p標(biāo)簽數(shù)組的寬度賦值為一個元素的寬度,我們可能寫出這樣的代碼:

function initP() {
    for (let i = 0; i < paragraphs.length; i++) {
        paragraphs[i].style.width = box.offsetWidth + 'px';
    }
}

這段代碼看上去是沒有什么問題,可是其實(shí)會造成很大的性能問題。在每次循環(huán)的時候,都讀取了box的一個offsetWidth屬性值,然后利用它來更新p標(biāo)簽的width屬性。這就導(dǎo)致了每一次循環(huán)的時候,瀏覽器都必須先使上一次循環(huán)中的樣式更新操作生效,才能響應(yīng)本次循環(huán)的樣式讀取操作。每一次循環(huán)都會強(qiáng)制瀏覽器刷新隊(duì)列。我們可以優(yōu)化為:

const width = box.offsetWidth;
function initP() {
    for (let i = 0; i < paragraphs.length; i++) {
        paragraphs[i].style.width = width + 'px';
    }
}

4、對于復(fù)雜動畫效果,使用絕對定位讓其脫離文檔流

對于復(fù)雜動畫效果,由于會經(jīng)常的引起回流重繪,因此,我們可以使用絕對定位,讓它脫離文檔流。否則會引起父元素以及后續(xù)元素頻繁的回流。例子

5、css3硬件加速(GPU加速)

使用css3硬件加速,可以讓transform、opacity、filters這些動畫不會引起回流重繪 。但是對于動畫的其它屬性,比如background-color這些,還是會引起回流重繪的,不過它還是可以提升這些動畫的性能。

如何使用

  • transform
  • opacity
  • filters
  • Will-change

效果

我們可以先看個例子。我通過使用chrome的Performance捕獲了一段時間的回流重繪情況,實(shí)際結(jié)果如下圖:

image

從圖中我們可以看出,在動畫進(jìn)行的時候,沒有發(fā)生任何的回流重繪。

重點(diǎn)

  • 使用css3硬件加速,可以讓transform、opacity、filters這些動畫不會引起回流重繪
  • 對于動畫的其它屬性,比如background-color這些,還是會引起回流重繪的,不過它還是可以提升這些動畫的性能。

css3硬件加速的坑

  • 如果你為太多元素使用css3硬件加速,會導(dǎo)致內(nèi)存占用較大,會有性能問題。
  • 在GPU渲染字體會導(dǎo)致抗鋸齒無效。這是因?yàn)镚PU和CPU的算法不同。因此如果你不在動畫結(jié)束的時候關(guān)閉硬件加速,會產(chǎn)生字體模糊。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容