佈局是瀏覽器計算各元素幾何信息的過程:元素的大小以及在頁面中的位置。 根據所用的 CSS、元素的內容或父級元素,每一個元素都將有顯式或隱含的大小信息。此過程在 Chrome、Opera、Safari 和 Internet Explorer 中稱爲佈局 (Layout)。 在 Firefox 中稱爲自動重排 (Reflow),但實際上其過程是同樣的。css
與樣式計算類似,佈局開銷的直接考慮因素以下:git
當您更改樣式時,瀏覽器會檢查任何更改是否須要計算佈局,以及是否須要更新渲染樹。對「幾何屬性」(如寬度、高度、左側或頂部)的更改都須要佈局計算。github
.box { width: 20px; height: 20px;}/** * Changing width and height * triggers layout. */.box--expanded { width: 200px; height: 350px;}
佈局幾乎老是做用到整個文檔。若是有大量元素,將須要很長時間來算出全部元素的位置和尺寸。web
若是沒法避免佈局,關鍵仍是要使用 Chrome DevTools 來查看佈局要花多長時間,並肯定佈局是否爲形成瓶頸的緣由。首先,打開 DevTools,選擇「Timeline」標籤,點擊「record」按鈕,而後與您的網站交互。當您中止記錄時,將看到網站表現狀況的詳細分析:瀏覽器
在仔細研究上例中的框架時,咱們看到超過 20 毫秒用在佈局上,當咱們在動畫中設置 16 毫秒來獲取屏幕上的框架時,此佈局時間太長。您還能夠看到,DevTools 將說明樹的大小(本例中爲 1618 個元素)以及須要佈局的節點數。安全
Note:想要一個有關哪些 CSS 屬性會觸發佈局、繪製或合成的確切列表?請查看CSS 觸發器。框架
網頁有各類佈局模型,一些模式比其餘模式受到更普遍的支持。最先的 CSS 佈局模型使咱們可以在屏幕上對元素進行相對、絕對定位或經過浮動元素定位。dom
下面的屏幕截圖顯示了在 1,300 個框上使用浮動的佈局開銷。固然,這是一我的爲的例子,由於大多數應用將使用各類手段來定位元素。函數
若是咱們更新此示例以使用 Flexbox(Web 平臺的新模型),則出現不一樣的狀況:佈局
如今,對於相同數量的元素和相同的視覺外觀,佈局的時間要少得多(本例中爲分別 3.5 毫秒和 14 毫秒)。務必記住,對於某些狀況,可能沒法選擇 Flexbox,由於它沒有浮動那麼受支持,可是在可能的狀況下,至少應研究佈局模型對網站性能的影響,而且採用最大程度減小網頁執行開銷的模型。
在任何狀況下,不論是否選擇 Flexbox,都應當在應用的高壓力點期間嘗試徹底避免觸發佈局!
將一幀送到屏幕會採用以下順序:
首先 JavaScript 運行,而後計算樣式,而後佈局。可是,可使用 JavaScript 強制瀏覽器提早執行佈局。這被稱爲強制同步佈局。
要記住的第一件事是,在 JavaScript 運行時,來自上一幀的全部舊佈局值是已知的,而且可供您查詢。所以,若是(例如)您要在幀的開頭寫出一個元素(讓咱們稱其爲「框」)的高度,可能編寫一些以下代碼:
// Schedule our function to run at the start of the frame.requestAnimationFrame(logBoxHeight);function logBoxHeight() { // Gets the height of the box in pixels and logs it out. console.log(box.offsetHeight);}
若是在請求此框的高度以前,已更改其樣式,就會出現問題:
function logBoxHeight() { box.classList.add('super-big'); // Gets the height of the box in pixels // and logs it out. console.log(box.offsetHeight);}
如今,爲了回答高度問題,瀏覽器必須先應用樣式更改(因爲增長了super-big
類),而後運行佈局。這時它才能返回正確的高度。這是沒必要要的,而且多是開銷很大的工做。
所以,始終應先批量讀取樣式並執行(瀏覽器可使用上一幀的佈局值),而後執行任何寫操做:
正確完成時,以上函數應爲:
function logBoxHeight() { // Gets the height of the box in pixels // and logs it out. console.log(box.offsetHeight); box.classList.add('super-big');}
大部分狀況下,並不須要應用樣式而後查詢值;使用上一幀的值就足夠了。與瀏覽器同步(或比其提早)運行樣式計算和佈局可能成爲瓶頸,而且您通常不想作這種設計。
有一種方式會使強制同步佈局甚至更糟:連續不斷地執行大量這種佈局。看看這個代碼:
function resizeAllParagraphsToMatchBlockWidth() { // Puts the browser into a read-write-read-write cycle. for (var i = 0; i < paragraphs.length; i++) { paragraphs[i].style.width = box.offsetWidth + 'px'; }}
此代碼循環處理一組段落,並設置每一個段落的寬度以匹配一個稱爲「box」的元素的寬度。這看起來沒有害處,但問題是循環的每次迭代讀取一個樣式值 (box.offsetWidth
),而後當即使用此值來更新段落的寬度 (paragraphs[i].style.width
)。在循環的下次迭代時,瀏覽器必須考慮樣式已更改這一事實,由於offsetWidth
是上次請求的(在上一次迭代中),所以它必須應用樣式更改,而後運行佈局。每次迭代都將出現此問題!
此示例的修正方法仍是先讀取值,而後寫入值:
// Read.var width = box.offsetWidth;function resizeAllParagraphsToMatchBlockWidth() { for (var i = 0; i < paragraphs.length; i++) { // Now write. paragraphs[i].style.width = width + 'px'; }}
若是要保證安全,應當查看FastDOM,它會自動爲您批處理讀取和寫入,應當能防止您意外觸發強制同步佈局或佈局抖動。