杭州下雪了,冷到不行,在家躺在牀上玩手機,打開微信進入前端交流羣裏平常吹水,看到大佬在羣裏發了一篇文章你應該要知道的重繪與重排,文章裏有一段騷操做,就是爲了減小重繪與重排,合併樣式操做,這個騷操做成功的引發了個人注意,而後開啓了個人探索。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;
。性能優化
首先回顧一下瀏覽器渲染頁面的流程:微信
而後在看看上面的過程,能夠容易看出,工具
Recalculate Style
,從新計算css規則樹。Layout
,這裏的Layout能夠理解成迴流,從新計算每一個元素的位置。Update Layer Tree
,字面意思理解,更新層級樹。Paint
,繪製頁面,在這裏能夠理解成重繪。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
過程進行的。
因爲paint
與composite
解耦,瀏覽器對每個layer
都有一個標識,這個標識用來標識該layer
是否須要重繪,在有CSS規則樹變化的時候,瀏覽器只會對這些被標識的layer進行重繪,用這樣的方式提升瀏覽器的渲染性能。
前端大法博大精深,越往下學越以爲本身不適合前端!!!彷彿看到本身在從入門到跑路這條路上快走到了終點。。。