整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          CSS-重繪 回流(Reflow &

          CSS-重繪 回流(Reflow & Repaint)

          流必將引起重繪,重繪不一定會引起回流。

          瀏覽器會把HTML解析成DOM,把CSS解析成CSSOM CSS Object Model即 CSSOM 和DOM合并就產生了Render Tree

          當Render Tree中部分或全部元素的尺寸、結構、或某些屬性發生改變時,瀏覽器重新渲染部分或全部文檔的過程稱為回流

          會導致回流的操作:

          頁面首次渲染

          瀏覽器窗口大小發生改變

          元素尺寸或位置發生改變

          元素內容變化(文字數量或圖片大小等等)

          元素字體大小變化

          添加或者刪除可見的DOM元素

          激活CSS偽類(例如::hover)

          查詢某些屬性或調用某些方法

          一些常用且會導致回流的屬性和方法:

          clientWidth、clientHeight、clientTop、clientLeft

          offsetWidth、offsetHeight、offsetTop、offsetLeft

          scrollWidth、scrollHeight、scrollTop、scrollLeft

          scrollIntoView()、scrollIntoViewIfNeeded()

          getComputedStyle()

          getBoundingClientRect()

          scrollTo()

          當頁面中元素樣式的改變并不影響它在文檔流中的位置時(例如:color、background-color、visibility等),瀏覽器會將新樣式賦予給元素并重新繪制它,這個過程稱為重繪。

          !!!回流比重繪的代價要更高

          當你訪問以下屬性或方法時,瀏覽器會立刻清空隊列:

          clientWidth、clientHeight、clientTop、clientLeft

          offsetWidth、offsetHeight、offsetTop、offsetLeft

          scrollWidth、scrollHeight、scrollTop、scrollLeft

          width、height

          getComputedStyle()

          getBoundingClientRect()

          如何避免

          CSS

          避免使用table布局。

          盡可能在DOM樹的最末端改變class。

          避免設置多層內聯樣式。

          將動畫效果應用到position屬性為absolute或fixed的元素上。

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

          JavaScript

          避免頻繁操作樣式,最好一次性重寫style屬性,或者將樣式列表定義為class并一次性更改class屬性。

          避免頻繁操作DOM,創建一個documentFragment,在它上面應用所有DOM操作,最后再把它添加到文檔中。

          也可以先為元素設置display: none,操作結束后再把它顯示出來。因為在display屬性為none的元素上進行的DOM操作不會引發回流和重繪。

          避免頻繁讀取會引發回流/重繪的屬性,如果確實需要多次使用,就用一個變量緩存起來。

          對具有復雜動畫的元素使用絕對定位,使它脫離文檔流,否則會引起父元素及后續元素頻繁回流

          能優化中,減少重繪重排應該是一種很好的優化方式,我們具體看一下什么情況下會造成重繪重排,為什么減少重繪重排可以做到優化,怎么樣減少重繪重排。

          瀏覽器渲染過程

          我們先看看當瀏覽器拿到服務端返回的資源時,是如何渲染的。

          首先瀏覽器會進行文件解析,主要解析三個東西:

          1. 解析 html/xhtml/svg,形成 dom 樹。
          2. 解析 css,產生 CSS Rule Tree。
          3. 解析 js,js 會通過 api(包括 DOM API 和 CSSOM API) 來操作 Dom Tree 和 CSS Rule Tree。

          解析完成之后

          1. 通過 CSS Rule Tree 和 Dom Tree 生成 rendering Tree。其中不包括 display:none 的元素。
          2. 計算 DOM 節點的位置,對元素進行分層及布局,也叫 reflow 和 layout 過程。

          布局完成之后,就要進行繪制了,將各層發給 GPU,GPU 將各層合成,顯示在屏幕上,即 composite。

          什么情況下會造成重繪重排

          當我們開始繪制的時候,如果使用 js 操作了 dom 元素,或者改變了 css 屬性,就可能會造成重繪(repaint)和重排(reflow)。

          repaint:屏幕的一部分進行了重畫,比如某個 css 中改變背景色,元素尺寸沒有變。 reflow:任何一個元素的尺寸發生了變化,需要重新驗證并計算 render tree,就會造成重排。

          在 PC 時代,我們用 jquery 進行獲取元素,改變元素的尺寸,及時發生重排,我們也很難感知到,但是當移動時代到來之后,如果頻繁發生重排,那手機就會受不了了。

          尤其是在執行下面操作時,成本會很高:

          1. js 添加或者刪除元素的時候
          2. js 改變元素的位置發生改變時
          3. 頁面初始化
          4. 容器尺寸發生變化。比如在標準盒模型下添加 padding,border 就會造成重排,所以在書寫樣式的時候,很多會將盒模型設計成怪異盒模型(box-sizing: border-box)
          5. js 獲取元素的尺寸單位。
          • offsetTop, offsetLeft, offsetWidth, offsetHeight
          • scrollTop/Left/Width/Height
          • clientTop/Left/Width/Height
          • IE 中的 getComputedStyle(), 或 currentStyle

          如果發生上述的行為基本都會造成重繪和重排。

          所以,當發生重排時,一定會發生重繪,但是發生重繪不一定會發生重排。

          瀏覽器中每個元素節點都有 reflow 方法,當一個元素發生 reflow 時,他的子節點都會發生 reflow。

          舉幾個例子來說明一下造成重繪重排的情況:

          var bodyStyle=document.body.style; // cache
          bodyStyle.padding='20px'; // reflow, repaint
          bodyStyle.border='10px solid red'; // 再一次的 reflow 和 repaint
          bodyStyle.color='blue'; // repaint
          bodyStyle.backgroundColor='#fad'; // repaint
          bodyStyle.fontSize='2em'; // reflow, repaint
          // new DOM element - reflow, repaint
          document.body.appendChild(document.createTextNode('children!'));
          

          為什么減少重繪重排可以優化性能

          前面說了瀏覽器的渲染機制,多一次重繪就需要瀏覽器重新進行一次繪制,及時 GPU 處理會比較快,但是也是吃不消的,更別說重排了,重排一個 dom,會重新生成 render Tree,然后重新繪制。

          如何減少重繪重排

          其實瀏覽器很聰明,不可能每次修改樣式就 reflow 或者 repaint 一次,一般來說,瀏覽器會積累一批操作,然后做一次 reflow。

          但是也有些例外情況,比如 resize 窗口,改變窗口字體,瀏覽器會立即進行 reflow。

          雖然瀏覽器會這么做,但是我們也應該減少重繪重排的次數,在開發階段就為瀏覽器進行特殊的關愛,畢竟是每天陪伴我們的小伙伴。

          下面總結了一些針對 reflow 和 repaint 的最佳實踐:

          1. 不要把 DOM 元素的屬性值放到一個循環中當成循環的變量。
          2. 盡可能的修改低層級的節點,避免修改高層級的節點,造成大面積的 reflow。
          3. 千萬不要使用 table 布局,修改一小塊地方,會造成整個 table 重新布局。
          4. 動畫盡量使用 css 動畫,css 動畫中盡量只使用 transform 和 opacity,這不會發生重排和重繪
          5. 不要一條一條的修改 DOM 樣式,盡量提前設置好 class,后續增加 class,進行批量修改。
          • 把 DOM 離線后修改。使用 documentFragment 對象在內存里操作 DOM。
          • 使用 requestAnimationFrame 可以進行優化,在下一幀進行操作。
          • 把修改頻繁的元素先 display: none,修改完之后顯示,修改個 100 次也無妨。
          • clone 一個 dom 節點在內存里,修改之后;與在線的節點相替換。

          composite

          當每次布局完成之后,就會發生 composite 過程,瀏覽器都把重繪后的圖像發給 GPU 去合成并顯示。

          在上面最佳實踐中最后提到了動畫,動畫其實是比較耗費性能的,因為動畫的每一幀都會發給 GPU 去合成,重繪重排會發生在動畫的每一幀。

          我們在寫動畫的時候,可以通過 js 寫,也可以通過 css 寫。兩種方式在寫動畫時,過程也是不一樣的。

          1. js 寫的動畫,過程:js 計算 -> 重排(若布局改變) -> 重繪 -> 合成
          2. css 動畫,過程:重排(若布局改變)-> 重繪 -> 合成

          所以不難看出,耗費性能最少并能并最流暢的動畫是只觸發合成。

          為了僅發生 composite,我們做動畫的 css property 必須滿足以下三個條件:

          1. 不影響文檔流。
          2. 不依賴文檔流。
          3. 不會造成重繪。

          滿足以上以上條件的 css property 只有 transform 和 opacity。

          這樣的話,由于沒有重排和重繪,只有合成,那么瀏覽器在動畫執行之前就知道動畫如何開始和結束。

          并且有兩個優勢:

          1. 動畫將會非常流暢
          2. 動畫不在綁定到 CPU,即使 js 執行大量的工作;動畫依然流暢

          事實上影響動畫流暢性的因素不止重排重繪,還有 CPU 內存。

          css 動畫有一個重要的特性,它是完全工作在 GPU 上。因為你聲明了一個動畫如何開始和如何結束,瀏覽器會在動畫開始前準備好所有需要的指令;并把它們發送給 GPU。

          而如果使用 js 動畫,瀏覽器必須計算每一幀的狀態;為了保證平滑的動畫,我們必須在瀏覽器主線程計算新狀態;把它們發送給 GPU 至少 60 次每秒。

          除了計算和發送數據比 css 動畫要慢,主線程的負載也會影響動畫; 當主線程的計算任務過多時,會造成動畫的延遲、卡頓。

          所以最佳實踐中最后一條就提到了,在寫動畫時,盡量寫 css 動畫,并且盡量用 transform 和 opacity。

          輔助工具

          谷歌瀏覽器檢測重繪工具:右上角三點->更多工具->開發者工具->Performance。

          chrome瀏覽器的Performance是頁面性能分析的利器,網上有很多關于關于如何去使用和查看Performance的文章,這里就不多做闡述了,大伙可以多去了解了解。

          小結

          總之,頁面性能優化是前端從初級到高級都避不開的一個話題,如何做到性能的最優化更是一個資深前端應該考慮的事情,這里也希望有更好更多見解的小伙伴能夠私聊我,給我點意見。

          文分享自華為云社區《前端頁面之“回流重繪”-云社區-華為云》,作者:CoderBin。

          “回流重繪”是什么?

          HTML中,每個元素都可以理解成一個盒子,在瀏覽器解析過程中,會涉及到回流與重繪:

          • 回流:布局引擎會根據各種樣式計算每個盒子在頁面上的大小與位置
          • 重繪:當計算好盒模型的位置、大小及其他屬性后,瀏覽器根據每個盒子特性進行繪制

          具體的瀏覽器解析渲染機制如下所示:

          • 解析HTML,生成DOM樹,解析CSS,生成CSSOM樹
          • 將DOM樹和CSSOM樹結合,生成渲染樹(Render Tree)
          • Layout(回流):根據生成的渲染樹,進行回流(Layout),得到節點的幾何信息(位置,大小)
          • Painting(重繪):根據渲染樹以及回流得到的幾何信息,得到節點的絕對像素
          • Display:將像素發送給GPU,展示在頁面上

          在頁面初始渲染階段,回流不可避免的觸發,可以理解成頁面一開始是空白的元素,后面添加了新的元素使頁面布局發生改變

          當我們對 DOM 的修改引發了 DOM幾何尺寸的變化(比如修改元素的寬、高或隱藏元素等)時,瀏覽器需要重新計算元素的幾何屬性,然后再將計算的結果繪制出來

          當我們對 DOM的修改導致了樣式的變化(colorbackground-color),卻并未影響其幾何屬性時,瀏覽器不需重新計算元素的幾何屬性、直接為該元素繪制新的樣式,這里就僅僅觸發了回流

          如何觸發

          要想減少回流和重繪的次數,首先要了解回流和重繪是如何觸發的

          回流觸發時機

          回流這一階段主要是計算節點的位置和幾何信息,那么當頁面布局和幾何信息發生變化的時候,就需要回流,如下面情況:

          • 添加或刪除可見的DOM元素
          • 元素的位置發生變化
          • 元素的尺寸發生變化(包括外邊距、內邊框、邊框大小、高度和寬度等)
          • 內容發生變化,比如文本變化或圖片被另一個不同尺寸的圖片所替代
          • 頁面一開始渲染的時候(這避免不了)
          • 瀏覽器的窗口尺寸變化(因為回流是根據視口的大小來計算元素的位置和大小的)

          還有一些容易被忽略的操作:獲取一些特定屬性的值

          offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight

          這些屬性有一個共性,就是需要通過即時計算得到。因此瀏覽器為了獲取這些值,也會進行回流

          除此還包括getComputedStyle方法,原理是一樣的

          重繪觸發時機

          觸發回流一定會觸發重繪

          可以把頁面理解為一個黑板,黑板上有一朵畫好的小花。現在我們要把這朵從左邊移到了右邊,那我們要先確定好右邊的具體位置,畫好形狀(回流),再畫上它原有的顏色(重繪)

          除此之外還有一些其他引起重繪行為:

          • 顏色的修改
          • 文本方向的修改
          • 陰影的修改

          瀏覽器優化機制

          由于每次重排都會造成額外的計算消耗,因此大多數瀏覽器都會通過隊列化修改并批量執行來優化重排過程。瀏覽器會將修改操作放入到隊列里,直到過了一段時間或者操作達到了一個閾值,才清空隊列

          當你獲取布局信息的操作的時候,會強制隊列刷新,包括前面講到的offsetTop等方法都會返回最新的數據

          因此瀏覽器不得不清空隊列,觸發回流重繪來返回正確的值

          三、如何減少

          我們了解了如何觸發回流和重繪的場景,下面給出避免回流的經驗:

          • 如果想設定元素的樣式,通過改變元素的 class 類名 (盡可能在 DOM 樹的最里層)
          • 避免設置多項內聯樣式
          • 應用元素的動畫,使用 position 屬性的 fixed 值或 absolute 值(如前文示例所提)
          • 避免使用 table 布局,table 中每個元素的大小以及內容的改動,都會導致整個 table 的重新計算
          • 對于那些復雜的動畫,對其設置 position: fixed/absolute,盡可能地使元素脫離文檔流,從而減少對其他元素的影響
          • 使用css3硬件加速,可以讓transformopacityfilters這些動畫不會引起回流重繪
          • 避免使用 CSS 的 JavaScript 表達式

          在使用 JavaScript 動態插入多個節點時, 可以使用DocumentFragment. 創建后一次插入. 就能避免多次的渲染性能

          但有時候,我們會無可避免地進行回流或者重繪,我們可以更好使用它們

          例如,多次修改一個把元素布局的時候,我們很可能會如下操作

          const el=document.getElementById('el')
          for(let i=0;i<10;i++) {
              el.style.top=el.offsetTop  + 10 + "px";
              el.style.left=el.offsetLeft + 10 + "px";
          }
          

          每次循環都需要獲取多次offset屬性,比較糟糕,可以使用變量的形式緩存起來,待計算完畢再提交給瀏覽器發出重計算請求

          // 緩存offsetLeft與offsetTop的值
          const el=document.getElementById('el') 
          let offLeft=el.offsetLeft, offTop=el.offsetTop
          
          // 在JS層面進行計算
          for(let i=0;i<10;i++) {
            offLeft +=10
            offTop  +=10
          }
          
          // 一次性將計算結果應用到DOM上
          el.style.left=offLeft + "px"
          el.style.top=offTop  + "px"
          

          我們還可避免改變樣式,使用類名去合并樣式

          const container=document.getElementById('container')
          container.style.width='100px'
          container.style.height='200px'
          container.style.border='10px solid red'
          container.style.color='red'
          

          使用類名去合并樣式

          <style>
            .basic_style {
                width: 100px;
                height: 200px;
                border: 10px solid red;
                color: red;
            }
          </style>
          <script>
            const container=document.getElementById('container')
            container.classList.add('basic_style')
          </script>
          

          前者每次單獨操作,都去觸發一次渲染樹更改(新瀏覽器不會),

          都去觸發一次渲染樹更改,從而導致相應的回流與重繪過程

          合并之后,等于我們將所有的更改一次性發出

          我們還可以通過通過設置元素屬性display: none,將其從頁面上去掉,然后再進行后續操作,這些后續操作也不會觸發回流與重繪,這個過程稱為離線操作

          點擊下方,第一時間了解華為云新鮮技術~

          華為云博客_大數據博客_AI博客_云計算博客_開發者中心-華為云


          主站蜘蛛池模板: 蜜臀AV无码一区二区三区| 亚洲av综合av一区| 国产一区二区三区日韩精品| 玩弄放荡人妻一区二区三区| 婷婷国产成人精品一区二| 国产一区二区三区国产精品| 亚洲综合国产一区二区三区| 国模精品一区二区三区视频| 99精品国产高清一区二区三区 | 精品无码一区在线观看| 亚洲免费视频一区二区三区| 伊人久久精品一区二区三区 | 中文字幕日韩一区二区三区不卡| 一区二区三区在线视频播放| 亚洲国产欧美日韩精品一区二区三区| 国产在线视频一区| 精品一区二区三区无码免费直播| 亚洲一区免费在线观看| 另类免费视频一区二区在线观看| 日本美女一区二区三区| 日韩福利视频一区| 中文激情在线一区二区| 精品无码人妻一区二区免费蜜桃| 国产av熟女一区二区三区| 亚洲一区二区视频在线观看| 国产精品久久久久一区二区| 中文无码一区二区不卡αv| 亚洲视频一区在线| 人妻无码久久一区二区三区免费| 丰满人妻一区二区三区视频53| 国产成人无码精品一区不卡| 国产成人欧美一区二区三区| 一区二区无码免费视频网站| 国内自拍视频一区二区三区| 久久无码人妻一区二区三区 | 亚洲国产高清在线一区二区三区| 韩国精品一区二区三区无码视频| 麻豆精品人妻一区二区三区蜜桃| 国产精品亚洲午夜一区二区三区 | 中文字幕一区二区三区5566| 一区二区三区福利视频免费观看|