前言,最近利用碎片時間拜讀了一下尼古拉斯的另外一巨做《高性能JavaScript》,今天寫的文章從「老生常談」的頁面重繪和重排入手,去探究這兩個概念在頁面性能提高上的做用。javascript
有經驗的大佬對這個概念必定不會陌生,「瀏覽器輸入URL發生了什麼」。估計你們已經爛熟於心了,從計算機網絡到JS引擎,一路飛奔到瀏覽器渲染引擎。 經驗越多就能理解的越深。感興趣的同窗能夠看一下這篇文章,深度和廣度俱佳 從輸入 URL 到頁面加載的過程?如何由一道題完善本身的前端知識體系!css
切回正題,咱們繼續探討何爲重排。瀏覽器下載完頁面全部的資源後,就要開始構建DOM樹,於此同時還會構建渲染樹(Render Tree)。(其實在構建渲染樹以前,和DOM樹同期會構建Style Tree。DOM樹與Style Tree合併爲渲染樹)前端
一旦渲染樹構建完成,就要開始繪製(paint)頁面元素了。當DOM的變化引起了元素幾何屬性的變化,好比改變元素的寬高,元素的位置,致使瀏覽器不得不從新計算元素的幾何屬性,並從新構建渲染樹,這個過程稱爲「重排」。完成重排後,要將從新構建的渲染樹渲染到屏幕上,這個過程就是「重繪」。簡單的說,重排負責元素的幾何屬性更新,重繪負責元素的樣式更新。並且,重排必然帶來重繪,可是重繪未必帶來重排。好比,改變某個元素的背景,這個就不涉及元素的幾何屬性,因此只發生重排。java
上面已經提到了,重排發生的根本原理就是元素的幾何屬性發生了改變,那麼咱們就從可以改變元素幾何屬性的角度入手segmentfault
重繪和重排的開銷是很是昂貴的,若是咱們不停的在改變頁面的佈局,就會形成瀏覽器耗費大量的開銷在進行頁面的計算,這樣的話,咱們頁面在用戶使用起來,就會出現明顯的卡頓。如今的瀏覽器其實已經對重排進行了優化,好比以下代碼:瀏覽器
var div = document.querySelector('.div'); div.style.width = '200px'; div.style.background = 'red'; div.style.height = '300px';
比較久遠的瀏覽器,這段代碼會觸發頁面2次重排,在分別設置寬高的時候,觸發2次,當代的瀏覽器對此進行了優化,這種思路相似於如今流行的MVVM框架使用的虛擬DOM,對改變的DOM節點進行依賴收集,確認沒有改變的節點,就進行一次更新。可是瀏覽器針對重排的優化雖然思路和虛擬DOM接近,可是仍是有本質的區別。大多數瀏覽器經過隊列化修改並批量執行來優化重排過程。也就是說上面那段代碼其實在如今的瀏覽器優化下,只構成一次重排。緩存
可是仍是有一些特殊的元素幾何屬性會形成這種優化失效。好比:性能優化
爲何形成優化失效呢?仔細看這些屬性,都是須要實時回饋給用戶的幾何屬性或者是佈局屬性,固然不能再依靠瀏覽器的優化,所以瀏覽器不得不當即執行渲染隊列中的「待處理變化」,並隨之觸發重排返回正確的值。網絡
接下來深刻的介紹幾種性能優化的小TIPS框架
既然重排&重繪是會影響頁面的性能,尤爲是糟糕的JS代碼更會將重排帶來的性能問題放大。既然如此,咱們首先想到的就是減小重排重繪。
考慮下面這個例子:
// javascript var el = document.querySelector('.el'); el.style.borderLeft = '1px'; el.style.borderRight = '2px'; el.style.padding = '5px';
這個例子其實和上面那個例子是一回事兒,在最糟糕的狀況下,會觸發瀏覽器三次重排。然鵝更高效的方式就是合併全部的改變一次處理。這樣就只會修改DOM節點一次,好比改成使用cssText屬性實現:
var el = document.querySelector('.el'); el.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px';
沿着這個思路,聰明的老鐵必定就說了,你直接改個類名不也妥妥的。沒錯,還有一種減小重排的方法就是切換類名,而不是使用內聯樣式的cssText方法。使用切換類名就變成了這樣:
// css .active { padding: 5px; border-left: 1px; border-right: 2px; } // javascript var el = document.querySelector('.el'); el.className = 'active';