掌握瀏覽器重繪(repaint)重排(reflow))-前端進階

不少人都知道要減小瀏覽器的重排和重繪,但對其中的具體原理以及如何具體操做並非很瞭解,當忽然提起這個話題的時候,仍是會一臉懵逼。但願你們能夠耐着性子閱讀本文,仔細琢磨,完全掌握這個知識點!css

博客前端積累文檔公衆號GitHub

網頁生成過程:

  1. HTML被HTML解析器解析成DOM 樹
  2. css則被css解析器解析成CSSOM 樹
  3. 結合DOM樹和CSSOM樹,生成一棵渲染樹(Render Tree)
  4. 生成佈局(flow),即將全部渲染樹的全部節點進行平面合成
  5. 將佈局繪製(paint)在屏幕上

第四步和第五步是最耗時的部分,這兩步合起來,就是咱們一般所說的渲染html

網上找了一張圖片,我加了註釋會更直觀一些:前端


渲染:

網頁生成的時候,至少會渲染一次git

在用戶訪問的過程當中,還會不斷從新渲染github

從新渲染須要重複以前的第四步(從新生成佈局)+第五步(從新繪製)或者只有第五個步(從新繪製)。web

重排比重繪大:

大,在這個語境裏的意思是:誰能影響誰?面試

  • 重繪:某些元素的外觀被改變,例如:元素的填充顏色
  • 重排:從新生成佈局,從新排列元素。

就如上面的概念同樣,單單改變元素的外觀,確定不會引發網頁從新生成佈局,但當瀏覽器完成重排以後,將會從新繪製受到這次重排影響的部分瀏覽器

好比改變元素高度,這個元素乃至周邊dom都須要從新繪製。

也就是說:"重繪"不必定會出現"重排","重排"必然會出現"重繪"緩存

重排(reflow):

概念:

當DOM的變化影響了元素的幾何信息(DOM對象的位置和尺寸大小),瀏覽器須要從新計算元素的幾何屬性,將其安放在界面中的正確位置,這個過程叫作重排。性能優化

重排也叫回流,重排的過程如下面這種理解方式更清晰一些:

迴流就比如向河裏(文檔流)扔了一塊石頭(dom變化),激起漣漪,而後引發周邊水流受到波及,因此叫作迴流

常見引發重排屬性和方法

任何會改變元素幾何信息(元素的位置和尺寸大小)的操做,都會觸發重排,下面列一些栗子:

  1. 添加或者刪除可見的DOM元素;
  2. 元素尺寸改變——邊距、填充、邊框、寬度和高度
  3. 內容變化,好比用戶在input框中輸入文字
  4. 瀏覽器窗口尺寸改變——resize事件發生時
  5. 計算 offsetWidth 和 offsetHeight 屬性
  6. 設置 style 屬性的值
常見引發重排屬性和方法
width height margin padding
display border position overflow
clientWidth clientHeight clientTop clientLeft
offsetWudth offsetHeight offsetTop offsetLeft
scrollWidth scrollHeight scrollTop scrollLeft
scrollIntoView() scrollTo() getComputedStyle()
getBoundingClientRect() scrollIntoViewIfNeeded()

重排影響的範圍:

因爲瀏覽器渲染界面是基於流失佈局模型的,因此觸發重排時會對周圍DOM從新排列,影響的範圍有兩種:

  • 全局範圍:從根節點html開始對整個渲染樹進行從新佈局。
  • 局部範圍:對渲染樹的某部分或某一個渲染對象進行從新佈局

全局範圍重排

<body>
  <div class="hello">
    <h4>hello</h4>
    <p><strong>Name:</strong>BDing</p>
    <h5>male</h5>
    <ol>
      <li>coding</li>
      <li>loving</li>
    </ol>
  </div>
</body>

當p節點上發生reflow時,hello和body也會從新渲染,甚至h5和ol都會收到影響。

局部範圍重排:

用局部佈局來解釋這種現象:把一個dom的寬高之類的幾何信息定死,而後在dom內部觸發重排,就只會從新渲染該dom內部的元素,而不會影響到外界。

儘量的減小重排的次數、重排範圍:

重排鬚要更新渲染樹,性能花銷很是大:

它們的代價是高昂的,會破壞用戶體驗,而且讓UI展現很是遲緩,咱們須要儘量的減小觸發重排的次數。

重排的性能花銷跟渲染樹有多少節點須要從新構建有關係:

因此咱們應該儘可能以局部佈局的形式組織html結構,儘量小的影響重排的範圍。

而不是像全局範圍的示例代碼同樣一溜的堆砌標籤,隨便一個元素觸發重排都會致使全局範圍的重排。

重繪(Repaints):

概念

當一個元素的外觀發生改變,但沒有改變佈局,從新把元素外觀繪製出來的過程,叫作重繪。

常見的引發重繪的屬性:

color border-style visibility background
text-decoration background-image background-position background-repeat
outline-color outline outline-style border-radius
outline-width box-shadow background-size

瀏覽器的渲染隊列:

思考如下代碼將會觸發幾回渲染?

div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';

根據咱們上文的定義,這段代碼理論上會觸發4次重排+重繪,由於每一次都改變了元素的幾何屬性,實際上最後只觸發了一次重排,這都得益於瀏覽器的渲染隊列機制

當咱們修改了元素的幾何屬性,致使瀏覽器觸發重排或重繪時。它會把該操做放進渲染隊列,等到隊列中的操做到了必定的數量或者到了必定的時間間隔時,瀏覽器就會批量執行這些操做。

強制刷新隊列:

div.style.left = '10px';
console.log(div.offsetLeft);
div.style.top = '10px';
console.log(div.offsetTop);
div.style.width = '20px';
console.log(div.offsetWidth);
div.style.height = '20px';
console.log(div.offsetHeight);

這段代碼會觸發4次重排+重繪,由於在console中你請求的這幾個樣式信息,不管什麼時候瀏覽器都會當即執行渲染隊列的任務,即便該值與你操做中修改的值沒關聯。

由於隊列中,可能會有影響到這些值的操做,爲了給咱們最精確的值,瀏覽器會當即重排+重繪

強制刷新隊列的style樣式請求

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight
  2. scrollTop, scrollLeft, scrollWidth, scrollHeight
  3. clientTop, clientLeft, clientWidth, clientHeight
  4. getComputedStyle(), 或者 IE的 currentStyle

咱們在開發中,應該謹慎的使用這些style請求,注意上下文關係,避免一行代碼一個重排,這對性能是個巨大的消耗

重排優化建議

就像上文提到的咱們要儘量的減小重排次數、重排範圍,這樣說很泛,下面是一些行之有效的建議,你們能夠參考一下。

1. 分離讀寫操做

div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
console.log(div.offsetLeft);
console.log(div.offsetTop);
console.log(div.offsetWidth);
console.log(div.offsetHeight);

仍是上面觸發4次重排+重繪的代碼,此次只觸發了一次重排:

在第一個console的時候,瀏覽器把以前上面四個寫操做的渲染隊列都給清空了。剩下的console,由於渲染隊列原本就是空的,因此並無觸發重排,僅僅拿值而已。

2. 樣式集中改變

div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';

雖然如今大部分瀏覽器有渲染隊列優化,不排除有些瀏覽器以及老版本的瀏覽器效率仍然低下:

建議經過改變class或者csstext屬性集中改變樣式

// bad
var left = 10;
var top = 10;
el.style.left = left + "px";
el.style.top  = top  + "px";
// good 
el.className += " theclassname";
// good
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";

3. 緩存佈局信息

// bad 強制刷新 觸發兩次重排
div.style.left = div.offsetLeft + 1 + 'px';
div.style.top = div.offsetTop + 1 + 'px';

// good 緩存佈局信息 至關於讀寫分離
var curLeft = div.offsetLeft;
var curTop = div.offsetTop;
div.style.left = curLeft + 1 + 'px';
div.style.top = curTop + 1 + 'px';

4. 離線改變dom

  • 隱藏要操做的dom

    在要操做dom以前,經過display隱藏dom,當操做完成以後,纔將元素的display屬性爲可見,由於不可見的元素不會觸發重排和重繪。

    dom.display = 'none'
    // 修改dom樣式
    dom.display = 'block'
  • 經過使用DocumentFragment建立一個dom碎片,在它上面批量操做dom,操做完成以後,再添加到文檔中,這樣只會觸發一次重排。
  • 複製節點,在副本上工做,而後替換它!

5. position屬性爲absolute或fixed

position屬性爲absolute或fixed的元素,重排開銷比較小,不用考慮它對其餘元素的影響

6. 優化動畫

  • 能夠把動畫效果應用到position屬性爲absolute或fixed的元素上,這樣對其餘元素影響較小

    動畫效果還應犧牲一些平滑,來換取速度,這中間的度本身衡量:

    好比實現一個動畫,以1個像素爲單位移動這樣最平滑,可是reflow就會過於頻繁,大量消耗CPU資源,若是以3個像素爲單位移動則會好不少。

  • 啓用GPPU加速

    此部分來自優化CSS重排重繪與瀏覽器性能

    GPU(圖像加速器):

    GPU 硬件加速是指應用 GPU 的圖形性能對瀏覽器中的一些圖形操做交給 GPU 來完成,由於 GPU 是專門爲處理圖形而設計,因此它在速度和能耗上更有效率。

    GPU 加速一般包括如下幾個部分:Canvas2D,佈局合成, CSS3轉換(transitions),CSS3 3D變換(transforms),WebGL和視頻(video)。

/*
 * 根據上面的結論
 * 將 2d transform 換成 3d
 * 就能夠強制開啓 GPU 加速
 * 提升動畫性能
 */
div {
  transform: translate3d(10px, 10px, 0);
}

結語

重排也是致使DOM腳本執行效率低的關鍵因素之一,重排與重繪做爲大廠常常出現的面試題,而且涉及的性能優化,這是前端必須掌握的基本概念/技能之一(敲黑板!)。

重排會不斷觸發這是不可避免的,但咱們在開發時,應儘可能按照文中的建議來組織代碼,這種優化,須要平時有意識的去作,一點一滴的去作,但願你們重視一下。

但願看完的朋友能夠點個喜歡/關注,您的支持是對我最大的鼓勵。

博客前端積累文檔公衆號GitHub

以上2018.12.17

參考資料:

網頁性能管理詳解

優化CSS重排重繪與瀏覽器性能

相關文章
相關標籤/搜索