Web效能優化系列:預防佈局抖動

劉健超-J.c發表於2015-06-24

預防“佈局抖動”

佈局抖動是因 JavaScript 的 DOM 元素被多被次暴力寫,然後讀,導致文件重排而出現的。

當DOM元素被寫入值,佈局就“無效”,而多次這樣就會導致文件重排。瀏覽器很懶,它總想等到當前操作(或幀)的最後一步才重排。

然而,如果在當前操作(幀)完成前,從DOM元素中獲取值,這會迫使瀏覽器提早執行佈局操作,這稱為“強制同步佈局”,這可是效能殺手!

佈局抖動的副作用在現代桌面瀏覽器上並不明顯;但對於低配置的移動裝置來說,其後果就不堪設想了。

能快速修復?

在理想情況下,我們可能通過簡單地重複執行,以至於將DOM元素的讀寫操作放在一起執行。這意味著文件只需重排一次即可。

現實情況會怎麼樣?

現實情況並非如此簡單。大型應用程式的程式碼會分散到各個地方,因此這些地方都有危險的DOM操作。所以不能簡單地(絕對不應該)聚集它們,而需要解耦程式碼,只是需要控制好執行順序。那如何讓讀寫操作捆綁在一起,從而獲得最佳效能呢?

進入requestAnimationFrame

window.requestAnimationFrame是一個將操作安排在下一幀一起執行的函式,類似於setTimeout(fn, 0)。這是非常有用的,因為能使用它來安排所有DOM的寫操作在下一幀一起執行,保留所有DOM的讀操作在當前同步狀態。

這意味著我們能很好地封裝程式碼了。經過小小調整後的程式碼,就將高耗能的DOM操作捆綁在一起!實在太棒了!

工作例項

我建立了一個工作案例來證明這個觀點。從第一個截圖的chrome時間軸可看出,有多個佈局抖動穿插其中。

Before After

在改用requestAnimationFrame 後,僅僅只觸發一次佈局事件,其結果是操作快了約96%。

它具有伸縮性嗎?

在一個簡單案例裡,使用requestAnimationFrame來延遲DOM寫操作,從而大大提高效能,但這項技術沒有伸縮性可言。

在我們的應用中,可能需要在DOM元素上執行先寫後讀操作,然後再次掉入佈局抖動的坑,只是在不同幀。

我們可以將讀操作放到另外一個requestAnimationFrame ,但我們不能保證應用程式的另一部分,沒有把寫操作放在同一幀上。

介紹 ‘FastDom’

FastDom是一個輕量的庫,它提供一個公共介面,能讓DOM的讀/寫操作捆綁在一起。其實,它就是利用上述同樣的 requestAnimationFrame 技術來大大提高DOM操作速度。

FastDom通過接收讀寫操作,並在下一幀捆綁它們(先讀後寫),從而消除DOM的相互影響。這意味著我們能獨立編寫應用程式元件,而不用擔心它們在應用程式中互相影響。

使用FastDom的啟示

通過使用FastDom,會讓所有DOM任務變成非同步,這意味著你不能總是假設DOM將會以什麼狀態進行操作。操作從之前的同步,變成現在的非同步方式。因此,可能沒執行完非同步處理函式就會執行下一步操作了。

要解決這一點,我打算用事件系統來明確操作何時完成,和明確依賴於完成後所做出的響應操作。

雖然所做工作是一樣的,但能通過增加程式碼量來顯著提高效能。我個人認為這個代價小。

FastDom案例

完善FastDom

web應用缺少一個明確的方式,來解決佈局抖動問題。正如一個應用程式很難協調所有不同的部分,來確保產品最終是高效的。如果FastDom能為開發者們提供一個簡單介面來解決這個問題,那隻能意味著它是個好東西。

瞧一瞧 FastDom 專案,歡迎隨時通過 pull requests 或 filing issues 來完善它。

打賞支援我翻譯更多好文章,謝謝!

打賞譯者

打賞支援我翻譯更多好文章,謝謝!

任選一種支付方式

Web效能優化系列:預防佈局抖動 Web效能優化系列:預防佈局抖動

相關文章