前端百題斬之從渲染流程認識重繪和回流
在“瀏覽器的渲染流程”一節中已經詳細闡述了渲染過程的幾個關鍵步驟,其簡要流程圖如下所示:
今天的主角“重繪和回流”就會導致瀏覽器觸發更新,重新進行渲染繪制,但是兩者稍有不同,重繪不會存在布局階段,而回流會進行重新布局,所以回流代價更高、損耗更大。
31.1 重繪
重繪是指頁面中某些元素發生了不影響布局的變化時(如顏色改變),瀏覽器重新繪制的過程。此時由于只需要UI層面的重新像素繪制,因此損耗較少。僅僅引發重繪的操作如下所示(注意:回流必定觸發重繪,但是重繪不一定觸發回流):
- 改變背景色;
- 改變文字顏色;
- 改變邊框顏色;
- 通過visibility:hidden隱藏元素;
- ……
31.2 回流
回流是指頁面中某些元素發生變化而影響了布局時(如尺寸、位置改變),瀏覽器需要重新布局并繪制的過程。引發回流的操作如下所示:
- 頁面初次渲染;
- 瀏覽器窗口大小改變;
- 元素尺寸、位置、內容發生改變;
- 元素字體大小變化;
- 添加或者刪除可見的 dom 元素;
- 激活 CSS 偽類(例如::hover);
- 查詢某些屬性或調用某些方法:
- clientWidth、clientHeight、clientTop、clientLeft
- offsetWidth、offsetHeight、offsetTop、offsetLeft
- scrollWidth、scrollHeight、scrollTop、scrollLeft
- getComputedStyle() :Window.getComputedStyle()方法返回一個對象,該對象在應用活動樣式表并解析這些值可能包含的任何基本計算后報告元素的所有CSS屬性的值。
- getBoundingClientRect()
- scrollTo():scrollTo() 方法可把內容滾動到指定的坐標。
31.3 減少回流和重繪
31.3.1 瀏覽器自身優化策略
由于每次重排都會造成額外的計算消耗,因此大多數瀏覽器都會通過隊列化修改并批量執行來優化重排過程。瀏覽器會將修改操作放入隊列里,直到過了一段時間或者操作達到了一個閾值,才清空隊列。當你獲取布局信息的操作的時候,會強制隊列刷新,比如訪問以下屬性或者使用以下方法:
- offsetTop、offsetLeft、offsetWidth、offsetHeight
- scrollTop、scrollLeft、scrollWidth、scrollHeight
- clientTop、clientLeft、clientWidth、clientHeight
- getComputedStyle()
- getBoundingClientRect
以上屬性和方法都需要返回最新的布局信息,因此瀏覽器不得不清空隊列,觸發回流重繪來返回正確的值。因此,在修改樣式的時候,最好避免使用上面列出的屬性,它們都會刷新渲染隊列。如果要使用它們,最好將值緩存起來。
另一優化就是瀏覽器認為position為absolute或fixed的元素更改只會影響其本身和子元素,而static的元素變化則會影響之后的所有元素。原因在于absolute和fixed認為元素從文檔流中清除了,怎么操作是內部的事。例如:對于復雜動畫效果,使用絕對定位讓其脫離文檔流
31.3.2 多次操作變為一次操作
不要一條一條的修改DOM的樣式,盡量使用class進行樣式修改。
把DOM離線修改(批量修改DOM)
(1)使用documentFragment對象在內存里操作DOM
(2)先把DOM給display:none,修改完畢再顯示出來
(3)clone一個DOM節點到內存里,然后想怎么改就怎么改,改完后,和在線的那個的交換一下。
31.3.3 其它
使用css3硬件加速,可以讓transform、opacity、filters(濾鏡)這些動畫不會引起回流重繪(注意:對于動畫的其它屬性,比如background-color這些,還是會引起回流重繪的,不過它還是可以提升這些動畫的性能)
不要把DOM結點的屬性值放在一個循環里當成循環里的變量。不然這會導致大量地讀寫這個結點的屬性。
千萬不要使用table布局。因為可能很小的一個小改動會造成整個table的重新布局。
本文轉載自微信公眾號「執鳶者」,可以通過以下二維碼關注。轉載本文請聯系執鳶者公眾號。