你將了解到:
什么是回流
什么是重繪
回流何時發生
重繪何時發生
如何避免回流和重繪
復制代碼
帶著上面的問題,我們一探究竟
什么是回流
回流:英文是reflow
當render tree中的一部分(或全部),因為元素的規模尺寸、布局、隱藏等改變
而需要重新構建,這就是回流(reflow)
復制代碼
- 每個頁面至少回流一次,即頁面首次加載
- 回流時,瀏覽器會使渲染樹中受到影響的部分失效,并重新構造這部分渲染樹
- 回流完成后,瀏覽器會重新繪制受影響的部分,是重繪過程
什么是重繪
重繪:英文是repaints
當render tree中的一些元素需要更新屬性,而這些屬性只是影響元素的外
觀、風格,而不影響布局(例如:background-color),則稱為重繪(repaints)
復制代碼
特點:回流必將引起重繪,重繪不一定引起回流 回流比重繪的代價更高
回流何時發生
當頁面布局和幾何屬性改變時就需要回流
下述情況會發生瀏覽器回流:
(1)添加或者刪除可見的DOM元素;
(2)元素位置改變;
(3)元素尺寸改變——邊距、填充、邊框、寬度和高度
(4)內容改變——比如文本改變或者圖片大小改變而引起的計算值寬度和高度改變;
(5)頁面渲染初始化;
(6)瀏覽器窗口尺寸改變——resize事件發生時;
復制代碼
let box = document.getElementById("box").style;
box.padding = "2px"; // 回流+重繪
box.border = "1px solid red"; // 再一次 回流+重繪
box.fontSize = "14px"; // 回流+重繪
document.getElementById("box").appendChild(document.createTextNode('abc!'));
復制代碼
重繪何時發生
元素的屬性或者樣式發生變化。
let box = document.getElementById("box").style;
box.color = "red"; // 重繪
box.backgroud-color = "blue"; // 重繪
document.getElementById("box").appendChild(document.createTextNode('abc!'));
復制代碼
因回流的開銷較大,如果每個操作都去回流重繪的話,瀏覽器可能就會受不了。所以很多瀏覽器都會優化這些操作。
多次的回流、重繪變成一次回流重繪:
瀏覽器會維護1個隊列,把所有會引起回流、重繪的操作放入這個隊列,等
隊列中的操作到了一定的數量或者到了一定的時間間隔,瀏覽器就會flush
隊列,進行一個批處理。
復制代碼
但是有時上面的方法會失效,原因是:
有些情況,當請求向瀏覽器請求一些style信息的時候,就會讓瀏覽器強制flush隊列,比如:(1)offsetTop, offsetLeft, offsetWidth, offsetHeight
(2) scrollTop/Left/Width/Height
(3)clientTop/Left/Width/Height
(4)width,height
(5)請求了getComputedStyle(), 或者 IE的 currentStyle
復制代碼
當你請求上面的一些屬性的時候,瀏覽器為了給你最精確的值,需要flush隊列,因為隊列中可能會有影響到這些值的操作。即使你獲取元素的布局和樣式信息跟最近發生或改變的布局信息無關,瀏覽器都會強行刷新渲染隊列。
這樣以來,瀏覽器的優化就顯得力不從心,所以我們需要一些方法,盡可能的避免或減少瀏覽器的回流、重繪
如何避免、減少回流和重繪
- 減少對render tree的操作【合并多次多DOM和樣式的修改】
- 減少對一些style信息的請求,盡量利用好瀏覽器的優化策略
(1)添加css樣式,而不是利用js控制樣式
(2)讓要操作的元素進行“離線處理”,處理完后一起更新當用DocumentFragment進行緩存操作,引發一次回流和重繪使用display:none技術,只引發兩次回流和重繪使用cloneNode(true or false)和replaceChild技術,引發一次回流和重繪
(3)直接改變className,如果動態改變樣式,則使用cssText(考慮沒有優化的瀏覽器)// badelem.style.left = x + "px";elem.style.top = y + "px";// goodelem.style.cssText += ";left: " + x + "px;top: " + y + "px;";
(4)不要經常訪問會引起瀏覽器flush隊列的屬性,如果你確實要訪問,利用緩存// badfor (var i = 0; i < len; i++) {el.style.left = el.offsetLeft + x + "px";el.style.top = el.offsetTop + y + "px";}// goodvar x = el.offsetLeft,y = el.offsetTop;for (var i = 0; i < len; i++) {x += 10;y += 10;el.style = x + "px";el.style = y + "px";}
(5)讓元素脫離動畫流,減少回流的Render Tree的規模$("#block1").animate({left:50});$("#block2").animate({marginLeft:50});
(6)將需要多次重排的元素,position屬性設為absolute或fixed,這樣此元素就脫離了文檔流,它的變化不會影響到其他元素。例如有動畫效果的元素就最好設置為絕對定位;
(7)避免使用table布局:盡量不要使用表格布局,如果沒有定寬表格一列的寬度由最寬的一列決定,那么很可能在最后一行的寬度超出之前的列寬,引起整體回流造成table可能需要多次計算才能確定好其在渲染樹中節點的屬性,通常要花3倍于同等元素的時間。
(8)盡量將需要改變DOM的操作一次完成let box = document.getElementById("box").style;// badbox.color = "red"; // 重繪box.size = "14px"; // 回流、重繪// goodbox.bord = '1px solid red'
(9)盡可能在DOM樹的最末端改變class,盡可能在DOM樹的里面改變class(可以限制回流的范圍)
(10)IE中避免使用JavaScript表達式
復制代碼
參考資料:
- 前端進階(二)重繪和回流
- 重繪與回流
- 頁面的重繪與回流,以及如何優化