原文位址:How (not) to trigger a layout in WebKit
正如大多數web開發者所知道的,大量的js腳本運作時間可能都耗費在DOM的操作上,而這些操作執行太耗時是由執行腳本引起的,而不是編譯執行js位元組碼本身所導緻。這其中一個潛在的耗時操作就是重排(又名回流)-- 浏覽器在渲染頁面的過程中會執行從頁面的DOM樹來構造一個渲染樹。DOM數量越多結構越複雜,操作越耗時。
讓頁面渲染和執行保持流暢的一個有效方法是進行批處理操作,讓DOM的操作、處理和狀态查詢區分開來(譯者注:狀态查詢就是擷取DOM的一些樣式屬性值,如擷取一個DIV元素的高度、寬度等)。
下面有些例子:
// 非最優, 會造成2次重排
var newWidth = aDiv.offsetWidth + 10; // Read
aDiv.style.width = newWidth + 'px'; // Write
var newHeight = aDiv.offsetHeight + 10; // Read
aDiv.style.height = newHeight + 'px'; // Write
// 更優,隻引起一次重排
var newWidth = aDiv.offsetWidth + 10; // Read
var newHeight = aDiv.offsetHeight + 10; // Read
aDiv.style.width = newWidth + 'px'; // Write
aDiv.style.height = newHeight + 'px'; // Write
Stoyan Stefanov在其部落格 Rendering: repaint, reflow/relayout, restyle中提供了關于這個話題的很好的例子。
這通常會導緻開發者有這樣的疑問:到底什麼會觸發重排?上周的時候, Dimitri Glazkov 在 this codesearch link這篇文章中回答了這個問題。我自己為了更好地去了解這個問題,我通讀了這篇文章,并把文章總結成了一系列的屬性和方法。
它們是:
元素(Element)
clientHeight, clientLeft, clientTop, clientWidth, focus(), getBoundingClientRect(), getClientRects(), innerText, offsetHeight, offsetLeft, offsetParent, offsetTop, offsetWidth, outerText, scrollByLines(), scrollByPages(), scrollHeight, scrollIntoView(), scrollIntoViewIfNeeded(), scrollLeft, scrollTop, scrollWidth
架構(Frame), 圖檔(Image)
height, width
範圍(Range)
getBoundingClientRect(), getClientRects()
SVGLocatable
computeCTM(), getBBox()
SVGTextContent
getCharNumAtPosition(), getComputedTextLength(), getEndPositionOfChar(), getExtentOfChar(), getNumberOfChars(), getRotationOfChar(), getStartPositionOfChar(), getSubStringLength(), selectSubString()
SVGUse
instanceRoot
window
getComputedStyle(), scrollBy(), scrollTo(), scrollX, scrollY, webkitConvertPointFromNodeToPage(), webkitConvertPointFromPageToNode()
這個清單覆寫範圍可能不全,但它是研究如何避免重排的一個好的開始。檢查是否觸發重排最好的方法是在Chrome或safari開發者工具的 Timeline panel選項中檢視紫色布局條的狀态。