應該知道的前端性能二三事 —— Reflow 和 Repaint

移動 Web 前端開發,目前是火的不能再火了。處處都在招什麼 H5 工程師、Hybrid App 開發工程師,主要負責的其實就是一些移動 Web 前端開發的工做。稍微有過一些前端經驗的人都知道,手機上的開銷比 PC 上要大的多,你在 PC 的模擬器上調試的很順暢,等到手機上時,就會卡,這是爲何呢?其實這就是性能問題,有其餘的開銷佔用了你的計算資源啦,那麼是哪些開銷佔用了呢?拋開後端接口慢啊、網絡狀態差啊什麼的不說,我們今天聊聊瀏覽器自己的細節,Reflow 和 Repaint。尤爲是在添加一些 CSS3 動畫或者批量操做多個 DOM 元素的時候,Reflow 和 Repaint 的影響就會更加大。javascript

啥是 Reflow 和 Repaint

Repaint

Repaint 就是「重繪」,它會在你改變 DOM 元素的視覺效果時進行,改變佈局時不會觸發。好比,opacity,background-color,visibilityoutline等都會觸發,「重繪」的開銷仍是比較昂貴的,由於瀏覽器會在某一個 DOM 元素的視覺效果改變後去 check 這個 DOM 元素內的全部節點。css

Reflow

Reflow 就是「迴流」,它的影響更大。它會在某一個 DOM 元素的位置發生改變後觸發,並且它會從新計算全部元素的位置和在頁面中的佔有的面積,這樣的話將會引發頁面某一個部分甚至整個頁面的從新渲染。改變某一個元素會影響它全部的子節點 (children)、祖先節點 (ancestors) 及兄弟節點(siblings)。html

Reflow 和 Repaint 都是瀏覽器慢的元兇

用戶和Web頁面都不能在 Reflow 和 Repaint 執行時作任何操做和響應,並且在極端的狀況下,CSS 會拖慢 JS 的執行速度,這就是瀏覽器有的時候就是在操做以後沒反應的緣由之一。前端

何時 Reflow 會觸發

Reflow 的開銷更加昂貴,那麼具體哪些時候會觸發 Reflow?java

  • 添加、刪除或者改變 DOM 元素的可見性時:使用 JS 去改變 DOM 元素時會觸發 Reflow。git

  • 添加、刪除或者改變 CSS 樣式:直接改變 CSS Style 或者元素的 class 可能會影響佈局,還有改變一個元素的寬度可以影響它所在的 DOM 節點中的全部元素,以及它周圍的那些元素。github

  • CSS3 動畫(animation)和過渡(transition): 動畫的每一 frame 都會觸發 Reflow。gulp

  • 使用 offsetWidth 和 offsetHeight:這一點很特別,你讀一個 DOM 的 offsetWidthoffsetHeight 屬性一樣會觸發一下 Reflow,由於這兩個屬性須要依賴一些元素去計算。後端

  • 用戶交互:用戶能夠經過 hover 一下 a 連接,在 input 裏面輸入文字,拖動瀏覽器的大小,改變字體大小,更換樣式表或者字體等都會觸發 reflow。瀏覽器

一些經常使用的提升性能的原則

1.不要用 inline style 或 table 佈局,flexbox 佈局也會給性能帶來一些小困擾。inline style 會在 html 下載完後進行一次額外的 Reflow,table佈局的開銷遠比其餘 DOM 元素的佈局開銷要大。flexbox 的 item 會在 HTML 下載完成後改變尺寸。

2.儘可能簡寫 CSS,避免使用複雜的 CSS 選擇器,使用 Unused CSS, uCSS, gulp-uncss能夠有效的減小樣式的定義和文件的大小。

3.減小 DOM 的層級,減小 DOM 的數量,若是不需適配老瀏覽器,刪掉一些無用的 wrapper 性質的 DOM 元素,總之越少越好。

4.在一個 DOM 樹中,儘量改那些沒有特別多子元素 DOM 的 class,子元素少的能夠改,多的不推薦。

5.刪掉複雜的動畫,運用動畫的元素儘可能是 position:absoluteposition:fixed 的,這樣會讓他們脫離文檔流,不去影響其餘的元素。

6.display:none 的元素不會引起 Reflow 和 Repaint,能夠在讓這些元素在 display 以前進行一些諸如顏色、尺寸什麼的改變。

7.批量去更新元素,好比下面這個例子會觸發3次 reflow:

var myelement = document.getElementById('myelement');
myelement.width = '100px';
myelement.height = '200px';
myelement.style.margin = '10px';

能夠用這種方式來優化上面的代碼:

var myelement = document.getElementById('myelement');
myelement.classList.add('newstyles');
.newstyles {
    width: 100px;
    height: 200px;
    margin: 10px;
}

此外,還能夠在生成新 DOM 時可使用 DOM fragment,而後先在內存中構建 DOM:

var
    i, li,
    frag = document.createDocumentFragment(),
    ul = frag.appendChild(document.createElement('ul'));

for (i = 1; i <= 3; i++) {
    li = ul.appendChild(document.createElement('li'));
    li.textContent = 'item ' + i;
}

document.body.appendChild(frag);

8.避免大量 DOM 之間互相影響,好比 Tabs 這種場景,若是你點擊一個 Tab 會顯示它控制的區塊,顯示的那個區塊會影響其餘的區塊,這樣可能會引發 Reflow,由於它們的高度不同,能夠經過定個高度來優化這種場景。

9.記住一個原則,平滑的動畫永遠都比不上性能的順暢重要,可用性永遠是第一位的,若是每一幀移動1像素會卡,就每一幀去移動10像素,必定不要讓性能降下來。

相關文章
相關標籤/搜索