頁面重繪和重拍版的效能問題

2022-03-05 03:13:44 字數 4425 閱讀 5166

當dom 改變影響到元素的幾何屬性(寬和高)——例如改變了邊框寬度或在段落中新增文字,將發生一系列後續動作——瀏覽器需要重新計算元素的幾何屬性,而且其他元素的幾何屬性和位置也會因此改變受到影響。瀏覽器使渲染樹上受到影響的部分失效,然後重構渲染樹。這個過程被稱作重排版。重排版完成時,瀏覽器在乙個重繪程序中重新繪製螢幕上受影響的部分。

不是所有的dom 改變都會影響幾何屬性。例如,改變乙個元素的背景顏色不會影響它的寬度或高度。在這種情況下,只需要重繪(不需要重排版),因為元素的布局沒有改變。

重繪和重排版是負擔很重的操作,可能導致網頁應用的使用者介面失去相應。所以,十分有必要盡可能減少這類事情的發生。

1)考慮下面這個例子,它改變同乙個風格屬性三次(這也許不是你在真正的**中所見到的,不過它孤立地展示出乙個重要話題):

// setting and retrieving styles in succession

var computed,

tmp = '',

bodystyle = document.body.style;

if (document.body.currentstyle) else

// inefficient way of modifying the same property

// and retrieving style information right after

bodystyle.color = 'red';

tmp = computed.backgroundcolor;

bodystyle.color = 'white';

tmp = computed.backgroundimage;

bodystyle.color = 'green';

tmp = computed.backgroundattachment;

在這個例子中,body 元素的前景色被改變了三次,每次改變之後,都匯入computed 的風格。匯入的屬性backgroundcolor, backgroundimage, 和backgroundattachment 與顏色改變無關。然而,瀏覽器需要重新整理渲染佇列並重排版,因為computed 的風格被查詢而引發。

比這個不講效率的例子更好的方法是不要在布局資訊改變時查詢它。如果將查詢computed 風格的**搬到末尾,**看起來將是這個樣子:

bodystyle.color = 'red';

bodystyle.color = 'white';

bodystyle.color = 'green';

tmp = computed.backgroundcolor;

tmp = computed.backgroundimage;

tmp = computed.backgroundattachment;

在所有瀏覽器上,第二個例子將更快,重排版和重繪代價昂貴,所以,提高程式響應速度乙個好策略是減少此類操作發生的機會。為減少發生次數,你應該將多個dom 和風格改變合併到乙個批次中一次性執行。

2)考慮這個例子:

var el = document.getelementbyid('mydiv');

el.style.borderleft = '1px';

el.style.borderright = '2px';

el.style.padding = '5px';

這裡改變了三個風格屬性,每次改變都影響到元素的幾何屬性。在這個糟糕的例子中,它導致瀏覽器重排版了三次。大多數現代瀏覽器優化了這種情況只進行一次重排版,但是在老式瀏覽器中,或者同時有乙個分離的同步程序(例如使用了乙個定時器),效率將十分低下。如果其他**在這段**執行時查詢布局資訊,將導致三次重布局發生。而且,此**訪問dom 四次,可以被優化。

乙個達到同樣效果而效率更高的方法是:將所有改變合併在一起執行,只修改dom 一次。可通過使用

csstext 屬性實現:

var el = document.getelementbyid('mydiv');

el.style.csstext = 'border-left: 1px; border-right: 2px; padding: 5px;';

這個例子中的**修改csstext 屬性,覆蓋已存在的風格資訊。如果你打算保持當前的風格,你可以將它附加在csstext 字串的後面。

el.style.csstext += '; border-left: 1px;';

另乙個一次性改變風格的辦法是修改css 的類名稱,而不是修改內聯風格**。這種方法適用於那些風格不依賴於執行邏輯,不需要計算的情況。改變css 類名稱更清晰,更易於維護;它有助於保持指令碼免除顯示**,雖然它可能帶來輕微的效能衝擊,因為改變類時需要檢查級聯表。

var el = document.getelementbyid('mydiv');

el.classname = 'active';

當你需要對dom 元素進行多次修改時,你可以通過以下步驟減少重繪和重排版的次數:

1.從文件流中摘除該元素

2.對其應用多重改變

3.將元素帶回文件中

此過程引發兩次重排版——第一步引發一次,第三步引發一次。如果你忽略了這兩個步驟,那麼第二步

中每次改變都將引發一次重排版。

有三種基本方法可以將dom 從文件中摘除:

1) 隱藏元素,進行修改,然後再顯示它。

2) 使用乙個文件片斷在已存dom 之外建立乙個子樹,然後將它拷貝到文件中。

3) 將原始元素拷貝到乙個脫離文件的節點中,修改副本,然後覆蓋原始元素。

為演示脫離文件操作,考慮這樣乙個鏈結列表,它必須被更多的資訊所更新:

假設附加資料已經儲存在乙個物件中了,需要插入到這個列表中。這些資料定義如下:      

var data = [

,];

下面是乙個通用的函式,用於將新資料更新到指定節點中:

var a, li;

for (var i = 0, max = data.length; i < max; i++)

};如前面所討論過的,減少重排版的乙個方法是通過改變display 屬性,臨時從文件上移除元素然後再恢復它。      

var ul = document.getelementbyid('mylist');

ul.style.display = 'none';

ul.style.display = 'block';

另一種減少重排版次數的方法是:在文件之外建立並更新乙個文件片斷,然後將它附加在原始列表上。文件片斷是乙個輕量級的document 物件,它被設計專用於更新、移動節點之類的任務。文件片斷乙個便利的語法特性是當你向節點附加乙個片斷時,實際新增的是文件片斷的子節點群,而不是片斷自己。下面的例子減少一行**,只引發一次重排版,只觸發「存在dom」一次。

var fragment = document.createdocumentfragment();

第三種解決方法首先建立要更新節點的副本,然後在副本上操作,最後用新節點覆蓋老節點:

var old = document.getelementbyid('mylist');

var clone = old.clonenode(true);

old.parentnode.replacechild(clone, old);

推薦盡可能使用文件片斷(第二種解決方案)因為它涉及最少數量的dom 操作和重排版。唯一潛在的缺點是,當前文件片斷還沒有得到充分利用,開發者可能不熟悉此技術。

瀏覽器通過佇列化修改和批量執行的方法,儘量減少重排版次數。當你查詢布局資訊如偏移量、滾動條位置,或風格屬性時,瀏覽器刷佇列並執行所有修改操作,以返回最新的數值。最好是儘量減少對布局資訊的查詢次數,查詢時將它賦給區域性變數,並用區域性變數參與計算。

考慮乙個例子,將元素myelement 向右下方向平移,每次乙個畫素,起始於100×100 位置,結束於500×500位置,在timeout 迴圈體中你可以使用:

// inefficient

myelement.style.left = 1 + myelement.offsetleft + 'px';

myelement.style.top = 1 + myelement.offsettop + 'px';

if (myelement.offsetleft >= 500)

這樣做很沒效率,因為每次元素移動,**查詢偏移量,導致瀏覽器重新整理渲染佇列,並沒有從優化中獲益。另乙個辦法只需要獲得起始位置值一次,將它存入區域性變數中var current = myelement.offsetleft;。然後,在動畫迴圈中,使用current 變數而不再查詢偏移量:

current++

myelement.style.left = current + 'px';

myelement.style.top = current + 'px';

if (current >= 500)

效能優化 頁面渲染 重繪 回流

參考 高效能web開發 深入理解頁面呈現 重繪 回流 因為一些原因要重新構建渲染數的時候,就叫做回流,第一次構建的時候也叫做回流。引起回流的操作 回流一定會導致重繪 影響元素幾何屬性發生變化 重繪 瀏覽器會使渲染樹中受到影響的部分失效,並重新構造這部分渲染樹,完成回流後,瀏覽器會重新繪製受影響的部分...

頁面回流和重繪的優化

在頁面中 js的執行和css的執行其實是兩條執行緒 我們只知道 最終的rendertree是由 domtree cssomtree js 的作用下 來生成的 但是js 的執行最終要依賴 domtree cssomtree的生成結果,這樣就導致了 css的執行 阻塞了js的執行,因此,我們把css 的...

頁面重繪 repaint 和回流 reflow

前言 頁面顯示到瀏覽器上的過程 1.1 生成乙個dom樹。瀏覽器將獲取到的html 解析成1個dom樹,包含了所有標籤,包括display none和動態新增的節點。1.2 生成樣式結構體。瀏覽器將所有樣式解析成樣式結構體,解析過程中會去掉瀏覽器不能識別的。2 dom樹和樣式結構體結合生成rende...