1. 什么是 repaint 和 reflow
一個頁面由兩部分組成:
DOM : 描述該頁面的結構
render : 描述 DOM 節點 (nodes) 在頁面上如何呈現
當 DOM 元素的屬性發生變化 (如 color) 時, 瀏覽器會通知 render 重新描繪相應的元素, 此過程稱為 repaint.
如果該次變化涉及元素布局 (如 width), 瀏覽器則拋棄原有屬性, 重新計算并把結果傳遞給 render 以重新描繪頁面元素, 此過程稱為 reflow.
這兩個過程是很耗費瀏覽器性能的, 從 IE 系列和 Chrome 渲染頁面速度上的差距即可看出渲染引擎計算對應值和呈現并不一定高效, 而每次對元素的操作都會發生 repaints 或 reflow, 因此編寫 DOM 交互時如果不注意就會導致頁面性能低下.
2. Reflow
當發生以下情況的時候, 會引發 reflow:
可見元素的增刪
元素的位置, 大小, 內容的改變
頁面第一次渲染
改變瀏覽器窗口的大小
幸運地, 瀏覽器對 reflow 的處理是和 PHP 的 Output Control 相似的 — 把 reflow 放進一個隊列, 達到一定程度或時限就進行 flush. 但不幸的是, 這個過程可能會被我們強制提前執行 — 只要使用下面所列的任一個都會迫使瀏覽器 flush, 因此產生 reflow:
offsetTop, offsetXXX…
scrollTop, scrollXXX…
clientTop, clientXXX…
getComputedStyle / currentStyle
3. 怎么優化
首先是減少對 DOM 中有關布局的操作, 假設我們要動態改變一個元素的大小為 100*100px:
操作 DOM 時盡量使用 DocumentFragment 和 cloneNode:
大致優化思路如下:
把元素從 DOM 流中剝離 (clone, fragment, position: absolute, 絕對定位對動畫或拖曳對象尤為有用)
在被剝離的的元素上進行各種操作
把被剝離的元素恢復到 DOM 流
注意:
IE lte 8 有一個 bug — :hover, 大量出現使用該選擇器的元素的話會降低頁面響應速度
在不鳥 older browsers 的情況下, 可以大量使用 firstElementChild(),querySelectorAll() 等 API, 可實現的功能和 jQuery 等庫的 CSS 選擇器和遍歷函數相差不大
此外對事件的綁定上盡可能使用 delegation, 也就是合理利用 event 的冒泡特性, 我認為這對 repaint & reflow 的影響是比較小的, 不過對于代碼優化, 復用性和靈活性都有很大的幫助, 有空再繼續