現代web框架大多都是數據驅動類的,好比 react, vue,因此開發者不須要直接接觸 DOM,修改 data 即可以驅動界面更新。可是做爲前端工程師,瞭解瀏覽器的重繪與重排仍是頗有必要的,能夠幫助咱們寫出更好性能的 web 應用。css
有了渲染樹(Render Tree),瀏覽器就知道網頁中有哪些節點,以及各個節點與 CSS 的關係,從而知道每一個節點的位置和幾何屬性,而後繪製頁面。前端
當 DOM 的變化影響了元素的幾何屬性(好比 width 和 height ),就會致使瀏覽器從新計算元素的幾何屬性,一樣受到該元素影響的其餘元素也會發生從新計算。此時,瀏覽器會使渲染樹中受到影響的部分失效,並從新構造渲染樹。這個過程被稱爲重排(也叫「迴流」)(reflow),完成重排以後,瀏覽器會從新繪製受影響的部分到頁面上,這個過程就是重繪(repaint)。vue
因此重排必定會引發重繪,而重繪不必定會引發重排,好比一個元素的改變並無影響佈局的改變(background-color的改變),在這種狀況下,只會發生一個重繪(不須要重排)。react
能夠總結出,當元素的幾何屬性或頁面佈局發生改變就會引發重排,好比:web
重繪與重排都是代價昂貴的操做(由於每次重排都會產生計算消耗),它們會致使 web 應用的 UI 反應遲鈍,因此開發者在編寫應用程序的時候應當儘可能減小這類過程的發生。瀏覽器
由於過多的重繪與重排可能會致使應用的卡頓,因此瀏覽器會對這個有一個優化的過程。大多數瀏覽器會經過隊列化來批量執行(好比把腳本對 DOM 的修改放入一個隊列,在隊列全部操做都結束後再進行一次繪製)。可是開發者有時可能不知不覺的強制刷新渲染隊列來當即進行重排重繪,好比獲取頁面佈局信息會致使渲染隊列的強制刷新,如下屬性或方法會當即觸發頁面繪製:緩存
以上屬性和方法都是要瀏覽器返回最新的佈局信息,因此瀏覽器會馬上執行渲染隊列中的「待處理變化」, 並觸發重排重繪而後返回最新的值。因此在修改樣式的過程當中,應該儘可能避免使用以上屬性和方法。前端工程師
爲了減小重繪重排的發生次數,開發者應該合併屢次對 DOM 的修改和對樣式的修改,而後一次性處理。數據結構
好比:app
var el = document.querySelector('div'); el.style.borderLeft = '1px'; el.style.borderRight = '2px'; el.style.padding = '5px';
能夠合併成:
var el = document.querySelector('div'); el.style.cssText = 'border-left: 1px; border-right: 1px; padding: 5px;'
使元素脫離文檔流,再對其進行操做,而後再把元素帶回文檔中,這種辦法能夠有效減小重繪重排的次數。有三種基本辦法能夠使元素脫離文檔流:
var ul = document.querySelector('ul'); ul.style.display = 'none'; // code... 對ul進行DOM操做 ul.style.display = 'block';
var fragment = document.createDocumentFragment(); // code... 對fragment進行DOM操做 var ul = document.querySelector('ul'); ul.appendChild(fragment)
var ul = document.querySelector('ul'); var cloneUl = ul.cloneNode(true); // code... 對clone節點進行DOM操做 ul.parentNode.replaceChild(cloneUl, ul)
前面已經知道,獲取頁面佈局信息,會致使瀏覽器強制刷新渲染隊列。因此減小這些操做是很是有必要的,開發者能夠將第一次獲取到的頁面信息緩存到局部變量中,而後再操做局部變量,好比下面的僞代碼示例:
// 低效的 element.style.left = 1 + element.offsetLeft + 'px'; element.style.top = 1 + element.offsetTop + 'px'; if (element.offsetTop > 500) { stopAnimation(); } // 高效的 var offsetLeft = element.offsetLeft; var offsetTop = element.offsetTop; offsetLeft++; offsetTop++; element.style.left = offsetLeft + 'px'; element.style.top = offsetTop + 'px'; if (offsetTop > 500) { stopAnimation(); }
爲了減小重繪重排帶來的性能消耗,能夠經過如下幾點改善 web 應用:
參考