迴流(reflow)與重繪(repaint),KFC與MC

本文被收納於: 從零開始的大前端築基之旅(深刻淺出,持續更新~)javascript

迴流與重繪,會影響頁面性能,每次這兩個都會被同時說起,關係就好像KFC邊上必定會有MC同樣親密的讓人摸不到頭腦。css

要理解迴流與重繪,首先要明白瀏覽器請求回來以後發生了什麼html

瀏覽器的渲染過程

在頁面加載時,瀏覽器渲染過程以下:前端

  1. 解析HTML,生成DOM樹,解析CSS,生成CSSOM樹
  2. 將DOM樹和CSSOM樹結合,生成渲染樹(Render Tree)
  3. Layout(迴流):根據生成的渲染樹,進行迴流(Layout),獲得節點的幾何信息(位置,大小)
  4. Painting(重繪):根據渲染樹以及迴流獲得的幾何信息,獲得節點的絕對像素
  5. Display:將像素髮送給GPU,展現在頁面上。(這一步其實還有不少內容,好比會在GPU將多個合成層合併爲同一個層,並展現在頁面中。而css3硬件加速的原理則是新建合成層)

爲了構建渲染樹,瀏覽器主要完成了如下工做:java

  1. 從DOM樹的根節點開始遍歷每一個可見節點。
  2. 對於每一個可見的節點,找到CSSOM樹中對應的規則,並應用它們。
  3. 根據每一個可見節點以及其對應的樣式,組合生成渲染樹。

渲染樹中只會包含可見節點。不可見節點包括:css3

  • 一些不會渲染輸出的節點,好比script、meta、link等。
  • 一些經過css進行隱藏的節點。如display:none。

注意,利用visibility和opacity隱藏的節點,仍是會顯示在渲染樹上的。segmentfault

因爲瀏覽器使用流式佈局,對Render Tree的計算一般只須要遍歷一次就能夠完成,但table及其內部元素除外,他們可能須要屢次計算瀏覽器

迴流

經過構造渲染樹,將可見DOM節點以及它對應的樣式結合起來,這時候須要計算它們在設備視口(viewport)內的確切位置和大小,這個計算的階段就是迴流。每一個頁面至少須要一次迴流,就是在頁面第一次加載的時候,這時候是必定會發生迴流的,由於要構建render tree。緩存

重繪

經過構造渲染樹和迴流階段,咱們肯定了可見節點,以及可見節點的樣式和具體的幾何信息(位置、大小),將渲染樹的每一個節點都轉換爲屏幕上的實際像素,這個階段就叫作重繪節點。wordpress

什麼時候發生迴流重繪

Render Tree中部分或所有元素的尺寸、結構、或某些屬性發生改變時,瀏覽器將從新渲染部分或所有文檔,這時發生的就是迴流。

會致使迴流的操做:

  • 頁面首次渲染
  • 瀏覽器窗口大小發生改變
  • 元素尺寸或位置發生改變(包括外邊距、內邊框、邊框大小、高度和寬度等)
  • 元素內容變化(文字數量或圖片大小,好比用戶在input框中輸入文字等等)
  • 元素字體大小變化
  • 添加或者刪除可見DOM元素
  • 激活CSS僞類(例如::hover)
  • 查詢某些屬性或調用某些方法

當頁面中元素樣式的改變並不影響它在文檔流中的位置時(例如:colorbackground-colorvisibility等),瀏覽器會將新樣式賦予給元素並從新繪製它,這個過程稱爲重繪。

迴流必定會觸發重繪,而重繪不必定會迴流

性能與優化

迴流比重繪的代價要高,至於具體的資源開銷跟render樹有多少節點須要從新構建有關。有時即便僅僅迴流一個單一的元素,它的父元素以及任何跟隨它的元素也會產生迴流。

大多數瀏覽器都會經過隊列化修改並批量執行來優化重排過程。:

瀏覽器會維護一個隊列,把全部引發迴流和重繪的操做放入隊列中,若是隊列中的任務數量或者時間間隔達到一個閾值的,瀏覽器就會將隊列清空,進行一次批處理,這樣能夠把屢次迴流和重繪變成一次。

可是!當你獲取佈局信息的操做的時候,會強制隊列刷新,好比當你訪問如下屬性或者使用如下方法:

  • offsetTop、offsetLeft、offsetWidth、offsetHeight
  • scrollTop、scrollLeft、scrollWidth、scrollHeight
  • clientTop、clientLeft、clientWidth、clientHeight
  • getComputedStyle()
  • getBoundingClientRect

由於隊列中可能會有影響到這些屬性或方法返回值的操做,即便你但願獲取的信息與隊列中操做引起的改變無關,瀏覽器也會強行清空隊列,確保你拿到的值是最精確的。

手動優化

  1. 避免頻繁操做樣式,最好一次性重寫style屬性,或者將樣式列表定義爲class並經過更改元素class屬性來應用樣式。

    經過style屬性設置樣式致使迴流。避免設置多級內聯樣式,由於每一個都會形成迴流,樣式應該合併在一個外部類,這樣當該元素的class屬性可被操控時僅會產生一個reflow。

  2. 避免頻繁操做DOM,建立一個documentFragment,在它上面應用全部DOM操做,最後再把它添加到文檔中。

    DocumentFragment 節點不屬於文檔樹,在把它插入文檔節點以前,隨便給他增刪節點都不會引發迴流

    使用cloneNode和replaceChild有相似的效果

  3. 先爲元素設置display: none,操做結束後再把它顯示出來。由於在display屬性爲none的元素上進行的DOM操做不會引起迴流和重繪。

  4. 避免頻繁讀取會引起迴流/重繪的屬性,若是確實須要屢次使用,就用一個變量緩存起來。

  5. 對具備複雜動畫的元素使用絕對定位,使它脫離文檔流,不然會引發父元素及後續元素頻繁迴流。

  6. 避免使用CSS表達式(例如:calc()

  7. 避免使用table佈局

    table是個和罕見的能夠影響在它們以前已經進入的DOM元素的顯示的元素。即便一些小的變化將致使表格(table)中的全部其餘節點回流。

  8. css3硬件加速(GPU加速)

    使用css3硬件加速,可讓transform、opacity、filters這些動畫不會引發迴流重繪

若是你收穫了新知識,或者收穫了左側精美圖片,請點個吧~

一個贊頂100閱讀量,告訴我你曾來過、看過,並在這裏不枉此行吧!!

以上內容參考了下面的資料,若有雷同,那很正常。。(這句話真不錯~)

參考文檔:

  1. 迴流(reflow)與重繪(repaint)
  2. 你真的瞭解迴流和重繪嗎
  3. 瀏覽器的迴流與重繪 (Reflow & Repaint)
  4. 迴流與重繪:CSS性能讓JavaScript變慢?
相關文章
相關標籤/搜索