關於迴流與重繪優化的探索

前言

杭州下雪了,冷到不行,在家躺在牀上玩手機,打開微信進入前端交流羣裏平常吹水,看到大佬在羣裏發了一篇文章你應該要知道的重繪與重排,文章裏有一段騷操做,就是爲了減小重繪與重排,合併樣式操做,這個騷操做成功的引發了個人注意,而後開啓了個人探索。css

正文

前言中描述的合併樣式的騷操做是以下:html

var el = document.querySelector('div');
el.style.borderLeft = '1px';
el.style.borderRight = '2px';
el.style.padding = '5px';

原文描述的大概意思是這段代碼屢次對 DOM 的修改和對樣式的修改,頁面會進行屢次迴流或者重繪,應該進行以下優化:前端

var el = document.querySelector('div');
el.style.cssText = 'border-left: 1px; border-right: 1px; padding: 5px;'

這樣的優化在之前我剛開始學習前端的時候,常常也在一些相關的性能優化的文章裏看到,由於一直沒有探究過,概念裏一直以爲本身應該把屢次 DOM 的樣式的修改合併在一塊兒,這樣效率會更高,直到後來,本身對瀏覽器的進程與線程慢慢有了瞭解,曾經也寫過一篇博客,淺談瀏覽器多進程與JS線程,其中有一個概念是,JS線程與GUI渲染線程是互斥關係,大概的意思就是當js引擎在執行js代碼的時候,瀏覽器的渲染引擎是被凍結了的,沒法渲染頁面的,必須等待js引擎空閒了才能渲染頁面。chrome

這個概念,JS線程與GUI渲染線程是互斥關係與上面描述的騷操做彷佛有點衝突,也就是當咱們對el.style進行一系列賦值的時候,渲染引擎是被凍結的狀態,怎麼會進行屢次重繪或者回流?帶着這樣的疑問,寫了一個小demo,代碼以下。canvas

<!DOCTYPE html>
<html>
<head>
  <title>測試頁</title>
  <style>
    #box {
      width: 109px;
      height: 100px;
      background-color: lightsteelblue;
      border-style: solid;
    }
  </style>
</head>
<body>
  <div id="box"></div>
</body>
<script>
var box = document.getElementById('box');
var toggle = 0;
var time = 500;
function toggleFun() {
  var borderWidth = toggle ? 20 : 0;
  var borderColor = toggle ? 'coral' : 'transparent';
  if (toggle) {
    box.style.borderWidth = '50px';
    box.style.borderWidth = borderWidth + 'px';
    box.style.borderColor = borderColor;
  } else {
    box.style.cssText = 'border: ' + borderWidth + 'px solid' + borderColor;
  }
  toggle = toggle ? 0 : 1;
}
setInterval(toggleFun, time)
</script>
</html>

代碼大概的意思就是定時以兩種操做設置樣式,收集瀏覽器的迴流或者重繪次數。segmentfault

打開chrome的開發者工具,切換到Performance選項卡,點擊左上角的圓 ○,開始record,等幾秒後stop,點擊Frames查看Event log選項卡,內容以下:瀏覽器

圖片描述

大概能夠看到,Recalculate Style -> Layout -> Update Layer Tree -> Paint -> Composite Layers 這個過程在循環進行,觸發的目標代碼是第25行代碼合29行代碼,也就是box.style.borderWidth = '50px';box.style.cssText = 'border: ' + borderWidth + 'px solid' + borderColor;性能優化

首先回顧一下瀏覽器渲染頁面的流程:微信

  1. 請求拿到html報文。
  2. 同時解析生成CSS規則樹和DOM樹。
  3. 合併CSS規則樹和DOM樹,生成render樹。
  4. 渲染進程根據render樹進行Layout。
  5. 繪製paint頁面。

而後在看看上面的過程,能夠容易看出,工具

  1. 首先,Recalculate Style,從新計算css規則樹。
  2. 進行Layout,這裏的Layout能夠理解成迴流,從新計算每一個元素的位置。
  3. Update Layer Tree,字面意思理解,更新層級樹。
  4. Paint,繪製頁面,在這裏能夠理解成重繪。
  5. Composite Layers,字面意思理解,合併層級。

由上面過程獲得結果,當在同一執行任務裏面對DOM的樣式進行屢次操做的時候,只會進行一次迴流或者重繪,也就是說,只要咱們的js引擎時候忙碌的,渲染引擎是凍結的時候,不管對DOM樣式進行多少次操做,都只會進行一次迴流或者重繪,也就是說前面說的合併樣式優化是無效的。

這個時候,我對上面過程又產生了新的疑問,爲何要Paint以後在Composite Layers呢?爲何不把全部層合併完了在繪製頁面呢?

.........................(看搜索相關資料去了)

翻看資料結束後,我獲得如下理解。

首先理解layer概念,能夠理解成PS裏面的圖層,咱們知道PS文件最後保存層PSD文件,當圖層越多的時候,PSD文件就越大,在咱們的瀏覽器裏面也是同樣的,咱們的layer越多,所佔的內存就越大。

而後理解Paint真正作的事情,paint的任務大概就是把全部的layer繪製到頁面中,這個繪製與canvas的繪製不同,canvas的繪製至關於在畫布裏把像素直接繪製成指定顏色,而後咱們直接看到的東西就直接是像素顏色,而咱們這裏說的Paint只是把圖層丟到頁面中,最後的繪製,須要交給Composite線程處理。

最後是Composite Layers,由composite線程進行,這個線程在瀏覽器的Renderer進程中,任務是把Paint時候丟上頁面的圖層轉化成位圖,最終生成咱們肉眼能夠看到的圖像,因此,真正的繪製,應該是Composite Layers過程進行的。

因爲paintcomposite解耦,瀏覽器對每個layer都有一個標識,這個標識用來標識該layer是否須要重繪,在有CSS規則樹變化的時候,瀏覽器只會對這些被標識的layer進行重繪,用這樣的方式提升瀏覽器的渲染性能。

最後

前端大法博大精深,越往下學越以爲本身不適合前端!!!彷彿看到本身在從入門到跑路這條路上快走到了終點。。。

參考

相關文章
相關標籤/搜索