迴流和重繪 的 面試與優化方案

迴流和重繪一直是前端工程師面試的常見,尤其是大廠的面試,畢竟前端頁面優化也是考覈開發者能力的關鍵之一。掌握迴流和重繪的概念,不單單是爲了面對各類面試,更能爲前端開發者在日常開發中提供優化方案。

理解迴流(reflow)和重繪(repaint)

首先,我們需要理解什麼是迴流和重繪,英文叫Reflow and Repaint 。

迴流 (Reflow ):當render樹中的一部分或者全部因爲大小邊距等問題發生改變而需要重建的過程叫做迴流;

重繪(Repaint ):當元素的一部分屬性發生變化,如外觀背景色不會引起佈局變化而需要重新渲染的過程叫做重繪。

例如修改背景顏色,字體顏色之類不影響佈局的行爲都只引發重繪。其餘情況可以看做爲是迴流重繪,迴流必將引起重繪,而重繪不一定會引起迴流。 所以,我們需要很清楚,頁面若發生迴流則需要付出很高的代價(會發生卡頓甚至頁面卡死)。

如何避免/減少迴流和重繪。

  1. 如果需要頻繁用JS操作DOM節點,可以使用 documentfragment ,這是一個純增加性能的方法;

  2. 在CSS屬性用法上,用 translate 代替 top ,因爲 top 會觸發迴流,但是translate不會。所以translate會比top節省了一個layout的時間;

  3. 在CSS屬性用法上,opacity 代替 visiability,會觸發重繪(paint),但opacity不會。該用法只針對於獨立圖層上;

  4. 若用JS去修改CSS的,最好別頻繁去操作DOM節點,最好把需要操作的樣式,提前寫成class,之後需要修改。只需要修改一次,需要修改的時候,直接修改className,做成一次性更新多條css DOM屬性,一次迴流重繪總比多次迴流重繪要付出的成本低得多;(谷歌瀏覽器有這個緩衝 flush 機制,如果在某個周內進行多次操作的話,會緩衝一次修改。)

  5. 離線迴流重繪,把需要回流重繪的節點,進行隱藏離線迴流重繪,display:none;

  6. 每次訪問DOM的偏移量屬性的時候,例如獲取一個元素的scrollTop、scrollLeft、scrollWidth、offsetTop、offsetLeft、offsetWidth、offsetHeight之類的屬性,瀏覽器爲了保證值的正確也會迴流取得最新的值,所以如果你要多次操作,最取完做個緩存。更加不要for循環中訪問DOM偏移量屬性,而且使用的時候,最好定義一個變量,把要需要的值賦值進去,進行值緩存,把迴流重繪的次數減少;

  7. 儘量少用table佈局,table佈局的話,每次有單元格佈局改變,都會進行整個tabel迴流重繪;

  8. 如果需要使用動畫,動畫速度根據平滑度和迴流重繪進行一下計算頁面性能的平衡;

  9. 把需要頻繁迴流重繪的單獨抽出去一個圖層,使用 transform:translateZ(0) 或 will-change:transform 的Css屬性都能實現;

  10. 啓用GPU加速,使用transform:translateZ(0) / transform:translate3d(0,0,0) 對性能提升很大。但是GPU的不能濫用,因爲開啓這個加速,會造成圖層過多,反而另render 樹對圖層合併的時間變長,適得其反。

重繪迴流優化方案總結

上面列了這麼多點,總結還是三點去優化:

  1. 避免使用觸發重繪迴流的CSS屬性。

  2. 儘量減少JS操作修改DOM的CSS次數。

  3. 將頻繁重繪迴流的DOM元素單獨作爲一個獨立圖層,那麼這個DOM元素的重繪和迴流影響只會在這個圖層中。頁面不能有過多圖層。

探討延伸

瀏覽器 flush 隊列機制

我們也知道迴流重繪的花銷也不小,如果每句JS操作都去迴流重繪的話,瀏覽器可能就會受不了。所以很多瀏覽器都會優化這些操作,瀏覽器會維護1個隊列,把所有會引起迴流、重繪的操作放入這個隊列,等隊列中的操作到了一定的數量或者到了一定的時間間隔,瀏覽器就會flush隊列,進行一個批處理。這樣就會讓多次的迴流、重繪變成一次迴流重繪。

雖然有了瀏覽器的優化,但有時候我們寫的一些代碼可能會強制瀏覽器提前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隊列,因爲隊列中可能會有影響到這些值的操作。即使你獲取元素的佈局和樣式信息跟最近發生或改變的佈局信息無關,瀏覽器都會強行刷新渲染隊列。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章