dev 中 gridcontrol1 滾動條重繪_瀏覽器的重繪和回流(Repaint amp; Reflow)

參考文獻:

https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-tree-construction?hl=zh-cn?developers.google.com你真的了解回流和重繪嗎 · Issue #4 · chenjigeng/blog?github.com
aa67834d7eeea99d9148297de88fe5d7.png

前言:

重繪:由于節點的幾何屬性發生改變或者由于樣式發生改變而不會影響布局的,稱為重繪,例如outline,visibility,colorbackground-color等。

回流:是布局或者幾何屬性需要改變就稱為回流。回流是影響瀏覽器性能的關鍵因素,因為其變化涉及到部分頁面(或是整個頁面)的布局更新。一個元素的回流可能會導致了其所有子元素以及DOM中緊隨其后的節點、祖先節點元素的隨后的回流。

測試:

分別使用left跟translate實現位移效果,查看重繪&回流對瀏覽器性能及視覺效果的影響。

1.left效果:

031f141efdc25fe17d6c2dfbb90cfea0.gif
left效果

2.translate效果:

ed64cc002c01699a8574c792fb887144.gif
translate效果

可以看到left一直在重繪&回流,但是translate就不一樣啦,這是因為CSS3做了很多優化。由此可見重繪&回流帶來的性能影響及糟糕的用戶體驗。

瀏覽器的渲染過程:

本文先從瀏覽器的渲染過程來從頭到尾的講解一下回流重繪,如果大家想直接看如何減少回流和重繪,可以跳到后面。(這個渲染過程來自MDN)

a494c4268e188b2aeee79234dbc5a55e.png

從上面這個圖上,我們可以看到,瀏覽器渲染過程如下:

  1. 解析HTML,生成DOM樹,解析CSS,生成CSSOM樹
  2. 將DOM樹和CSSOM樹結合,生成渲染樹(Render Tree)
  3. Layout(回流):根據生成的渲染樹,進行回流(Layout),得到節點的幾何信息(位置,大小)
  4. Painting(重繪):根據渲染樹以及回流得到的幾何信息,得到節點的絕對像素
  5. Display:將像素發送給GPU,展示在頁面上。(這一步其實還有很多內容,比如會在GPU將多個合成層合并為同一個層,并展示在頁面中。而css3硬件加速的原理則是新建合成層,這里我們不展開,之后有機會會寫一篇博客)

渲染過程看起來很簡單,讓我們來具體了解下每一步具體做了什么。

生成渲染樹:

cd37df831aa1fc08c1d3223e25d9288f.png

為了構建渲染樹,瀏覽器主要完成了以下工作:

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

第一步中,既然說到了要遍歷可見的節點,那么我們得先知道,什么節點是不可見的。不可見的節點包括:

  • 一些不會渲染輸出的節點,比如script、meta、link等。
  • 一些通過css進行隱藏的節點。比如display:none。注意,利用visibility和opacity隱藏的節點,還是會顯示在渲染樹上的。只有display:none的節點才不會顯示在渲染樹上。

注意:渲染樹只包含可見的節點

回流:

前面我們通過構造渲染樹,我們將可見DOM節點以及它對應的樣式結合起來,可是我們還需要計算它們在設備視口(viewport)內的確切位置和大小,這個計算的階段就是回流。

為了弄清每個對象在網站上的確切大小和位置,瀏覽器從渲染樹的根節點開始遍歷,我們可以以下面這個實例來表示:

<!DOCTYPE html>
<html><head><meta name="viewport" content="width=device-width,initial-scale=1"><title>Critial Path: Hello world!</title></head><body><div style="width: 50%"><div style="width: 50%">Hello world!</div></div></body>
</html>

我們可以看到,第一個div將節點的顯示尺寸設置為視口寬度的50%,第二個div將其尺寸設置為父節點的50%。而在回流這個階段,我們就需要根據視口具體的寬度,將其轉為實際的像素值。(如下圖)

dca751414a72489e0ba7d6d07499b5e0.png

重繪

最終,我們通過構造渲染樹和回流階段,我們知道了哪些節點是可見的,以及可見節點的樣式和具體的幾何信息(位置、大小),那么我們就可以將渲染樹的每個節點都轉換為屏幕上的實際像素,這個階段就叫做重繪節點。

既然知道了瀏覽器的渲染過程后,我們就來探討下,何時會發生回流重繪。

何時發生回流重繪

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

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

注意:回流一定會觸發重繪,而重繪不一定會回流

根據改變的范圍和程度,渲染樹中或大或小的部分需要重新計算,有些改變會觸發整個頁面的重排,比如,滾動條出現的時候或者修改了根節點。

瀏覽器的優化機制

現代的瀏覽器都是很聰明的,由于每次重排都會造成額外的計算消耗,因此大多數瀏覽器都會通過隊列化修改并批量執行來優化重排過程。瀏覽器會將修改操作放入到隊列里,直到過了一段時間或者操作達到了一個閾值,才清空隊列。但是!當你獲取布局信息的操作的時候,會強制隊列刷新,比如當你訪問以下屬性或者使用以下方法:

  • offsetTop、offsetLeft、offsetWidth、offsetHeight
  • scrollTop、scrollLeft、scrollWidth、scrollHeight
  • clientTop、clientLeft、clientWidth、clientHeight
  • getComputedStyle()
  • getBoundingClientRect
  • 具體可以訪問這個網站:https://gist.github.com/paulirish/5d52fb081b3570c81e3a

以上屬性和方法都需要返回最新的布局信息,因此瀏覽器不得不清空隊列,觸發回流重繪來返回正確的值。因此,我們在修改樣式的時候,**最好避免使用上面列出的屬性,他們都會刷新渲染隊列。**如果要使用它們,最好將值緩存起來。

減少回流和重繪

好了,到了我們今天的重頭戲,前面說了這么多背景和理論知識,接下來讓我們談談如何減少回流和重繪。

最小化重繪和重排

由于重繪和重排可能代價比較昂貴,因此最好就是可以減少它的發生次數。為了減少發生次數,我們可以合并多次對DOM和樣式的修改,然后一次處理掉。考慮這個例子

const el = document.getElementById('test');
el.style.padding = '5px';
el.style.borderLeft = '1px';
el.style.borderRight = '2px';

例子中,有三個樣式屬性被修改了,每一個都會影響元素的幾何結構,引起回流。當然,大部分現代瀏覽器都對其做了優化,因此,只會觸發一次重排。但是如果在舊版的瀏覽器或者在上面代碼執行的時候,有其他代碼訪問了布局信息(上文中的會觸發回流的布局信息),那么就會導致三次重排。

因此,我們可以合并所有的改變然后依次處理,比如我們可以采取以下的方式:

  • 使用cssText
    const el = document.getElementById('test'); el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px;';
  • 修改CSS的class
    const el = document.getElementById('test'); el.className += ' active';

批量修改DOM

當我們需要對DOM對一系列修改的時候,可以通過以下步驟減少回流重繪次數:

  1. 使元素脫離文檔流
  2. 對其進行多次修改
  3. 將元素帶回到文檔中。

該過程的第一步和第三步可能會引起回流,但是經過第一步之后,對DOM的所有修改都不會引起回流,因為它已經不在渲染樹了。

有三種方式可以讓DOM脫離文檔流:

  • 隱藏元素,應用修改,重新顯示
  • 使用文檔片段(document fragment)在當前DOM之外構建一個子樹,再把它拷貝回文檔。
  • 將原始元素拷貝到一個脫離文檔的節點中,修改節點后,再替換原始的元素。

考慮我們要執行一段批量插入節點的代碼:

function appendDataToElement(appendToElement, data) {let li;for (let i = 0; i < data.length; i++) {li = document.createElement('li');li.textContent = 'text';appendToElement.appendChild(li);}
}const ul = document.getElementById('list');
appendDataToElement(ul, data);

如果我們直接這樣執行的話,由于每次循環都會插入一個新的節點,會導致瀏覽器回流一次。

我們可以使用這三種方式進行優化:

隱藏元素,應用修改,重新顯示

這個會在展示和隱藏節點的時候,產生兩次重繪

function appendDataToElement(appendToElement, data) {let li;for (let i = 0; i < data.length; i++) {li = document.createElement('li');li.textContent = 'text';appendToElement.appendChild(li);}
}
const ul = document.getElementById('list');
ul.style.display = 'none';
appendDataToElement(ul, data);
ul.style.display = 'block';

使用文檔片段(document fragment)在當前DOM之外構建一個子樹,再把它拷貝回文檔

const ul = document.getElementById('list');
const fragment = document.createDocumentFragment();
appendDataToElement(fragment, data);
ul.appendChild(fragment);

將原始元素拷貝到一個脫離文檔的節點中,修改節點后,再替換原始的元素。

const ul = document.getElementById('list');
const clone = ul.cloneNode(true);
appendDataToElement(clone, data);
ul.parentNode.replaceChild(clone, ul);

對于上述那種情況,我寫了一個demo來測試修改前和修改后的性能。然而實驗結果不是很理想。

原因:原因其實上面也說過了,瀏覽器會使用隊列來儲存多次修改,進行優化,所以對這個優化方案,我們其實不用優先考慮。

避免觸發同步布局事件

上文我們說過,當我們訪問元素的一些屬性的時候,會導致瀏覽器強制清空隊列,進行強制同步布局。舉個例子,比如說我們想將一個p標簽數組的寬度賦值為一個元素的寬度,我們可能寫出這樣的代碼:

function initP() {for (let i = 0; i < paragraphs.length; i++) {paragraphs[i].style.width = box.offsetWidth + 'px';}
}

這段代碼看上去是沒有什么問題,可是其實會造成很大的性能問題。在每次循環的時候,都讀取了box的一個offsetWidth屬性值,然后利用它來更新p標簽的width屬性。這就導致了每一次循環的時候,瀏覽器都必須先使上一次循環中的樣式更新操作生效,才能響應本次循環的樣式讀取操作。每一次循環都會強制瀏覽器刷新隊列。我們可以優化為:

const width = box.offsetWidth;
function initP() {for (let i = 0; i < paragraphs.length; i++) {paragraphs[i].style.width = width + 'px';}
}

同樣,我也寫了個demo來比較兩者的性能差異。你可以自己點開這個demo體驗下。這個對比差距就比較明顯。

對于復雜動畫效果,使用絕對定位讓其脫離文檔流

對于復雜動畫效果,由于會經常的引起回流重繪,因此,我們可以使用絕對定位,讓它脫離文檔流。否則會引起父元素以及后續元素頻繁的回流。這個我們就直接上個例子。

打開這個例子后,我們可以打開控制臺,控制臺上會輸出當前的幀數(雖然不準)。

a9cd424f59d519368a08143bce35471a.png

從上圖中,我們可以看到,幀數一直都沒到60。這個時候,只要我們點擊一下那個按鈕,把這個元素設置為絕對定位,幀數就可以穩定60。

css3硬件加速(GPU加速):

比起考慮如何減少回流重繪,我們更期望的是,根本不要回流重繪。這個時候,css3硬件加速就閃亮登場啦!!

劃重點:使用css3硬件加速,可以讓transform、opacity、filters這些動畫不會引起回流重繪 。但是對于動畫的其它屬性,比如background-color這些,還是會引起回流重繪的,不過它還是可以提升這些動畫的性能。

本篇文章只討論如何使用,暫不考慮其原理,之后有空會另外開篇文章說明。

如何使用

常見的觸發硬件加速的css屬性:

  • transform
  • opacity
  • filters
  • Will-change

效果

我們可以先看個例子。我通過使用chrome的Performance捕獲了一段時間的回流重繪情況,實際結果如下圖:

45b309339b1177313c60c5d99f1c9e57.png

從圖中我們可以看出,在動畫進行的時候,沒有發生任何的回流重繪。如果感興趣你也可以自己做下實驗。

重點

  • 使用css3硬件加速,可以讓transform、opacity、filters這些動畫不會引起回流重繪
  • 對于動畫的其它屬性,比如background-color這些,還是會引起回流重繪的,不過它還是可以提升這些動畫的性能。

css3硬件加速的坑

  • 如果你為太多元素使用css3硬件加速,會導致內存占用較大,會有性能問題。
  • 在GPU渲染字體會導致抗鋸齒無效。這是因為GPU和CPU的算法不同。因此如果你不在動畫結束的時候關閉硬件加速,會產生字體模糊。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/534648.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/534648.shtml
英文地址,請注明出處:http://en.pswp.cn/news/534648.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

礦井通風計算c語言_礦井通風機主要參數的含義

礦井通風機的作用就是把地面新鮮空氣送到井下&#xff0c;供工人呼吸&#xff0c;同時把有害氣體從井下排出&#xff0c;使有害氣體的濃度降到對人體無害的程度&#xff0c;在現代化煤礦中稱通風機為“礦井的肺臟”&#xff0c;可見其重要性。風機的參數是風機選型的唯一依據&a…

行健設計_行健要聞|“第四屆‘天行健創新創業設計大賽”培訓班成功舉辦

10月9日下午&#xff0c;由院團委主辦、商學部承辦第四屆“天行健”創新創業設計大賽動員大會暨首場培訓講座在教學樓4-103室成功啟動。院團委副書記唐典巧參加動員會&#xff0c;并頒發了第九屆“挑戰杯”廣西大學生課外學術科技作品競賽榮譽證書&#xff0c;動員會由商學部輔…

sql同時操作兩列_怎么在兩列同時篩選數據庫

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云數據庫專家保駕護航&#xff0c;為用戶…

ip地址轉換pta題目_PTA「實驗2-3-5 輸出華氏-攝氏溫度轉換表」

PTA是浙江大學設計類實驗輔助教學平臺。題目描述輸入2個正整數lower和upper&#xff08;lower≤upper≤100&#xff09;&#xff0c;請輸出一張取值范圍為[lower&#xff0c;upper]、且每次增加2華氏度的華氏-攝氏溫度轉換表。溫度轉換的計算公式&#xff1a;C5(F?32)/9&#…

語言爬蟲字段為空_我為什么建議前端將Python 作為第二語言?

前言本文的文字及圖片來源于網絡,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯系我們以作處理。作者&#xff1a; 前端勸退師PS&#xff1a;如有需要Python學習資料的小伙伴可以加點擊下方鏈接自行獲取http://note.youdao.com/noteshare?id3054c…

5 獲取當前訪問的控制名稱_LabVIEW編程技巧:網絡通信中如何獲取計算機名稱、IP地址等信息...

問題引出在網絡通訊編程中&#xff0c;經常需要獲取當前主機的名稱、IP地址等信息&#xff0c;在LabVIEW中如何獲取這些信息呢&#xff1f;下面以本機為例進行說明&#xff0c;先看一下本機的信息。在Windows系統中打開控制臺程序&#xff0c;輸入“ipconfig /all”指令&#x…

ios 通過kvc修改屬性會觸發kvo_iOS開發-KVC和KVO的理解

KVC和KVO看起來很專業&#xff0c;其實用起來還是比較簡單的,KVC(Key-value coding)可以理解為鍵值對編碼&#xff0c;如果對象的基本類型&#xff0c;那么鍵值對編碼實際上和get&#xff0c;set方法沒有區別&#xff0c;如果是屬性是另外一個對象&#xff0c;那么發現KVC用起來…

python stdin和stdout_stdin似乎比stdout(python)慢得多.為什么?

我有兩個需要相互通信的python程序(一個是子進程).目前我通過stdin和stdout這樣做.然而,寫入子進程的標準似乎很慢.a.py,一個采用任意行輸入并打印時間的程序&#xff1a;from time import time, sleepfrom sys import stdout, stdinwhile True:stdin.readline()stdout.write(%…

postman生成python代碼_別再用手敲了,這個工具可以自動生成python爬蟲代碼

我們在寫爬蟲代碼時&#xff0c;常常需要各種分析調試&#xff0c;而且每次直接用代碼調試都很麻煩所以今天給大家分享一個工具&#xff0c;不僅能方便模擬發送各種http請求&#xff0c;還能輕松調試&#xff0c;最重要的是&#xff0c;可以將調試最終結果自動轉換成爬蟲代碼&a…

電工結業試卷_電工技術基礎結業考試試卷

電工技術基礎結業考試試卷適用年級2016級秋6班電子、計算機專業班級 姓名 學號一、 判斷題(每題2分&#xff0c;共30分)1、 電位越高則電壓越大。2、萬用表的電壓、電路及電阻檔的刻度都是均勻的。3、 在任何情況下&#xff0c;電壓源和電流源之間總是可以等效變換的。 4、 電容…

表格里面怎么打多個√_Excel怎樣在表格里打√?

我們做表的時候有時會遇到輸入對或錯的情況。Excel表格里面輸入√和&#xff0c;普通的方法是直接插入特殊符號來進行對勾的輸入。這樣輸入起來1個2個還可以接受&#xff0c;但是如果頻繁輸入&#xff0c;那我們得想想簡單方法。比如我們直接在excel單元格里面輸入1顯示√&…

@value 默認值為null_JAVA8之妙用Optional解決判斷Null為空的問題

引言在文章的開頭&#xff0c;先說下NPE問題&#xff0c;NPE問題就是&#xff0c;我們在開發中經常碰到的NullPointerException.假設我們有兩個類&#xff0c;他們的UML類圖如下圖所示在這種情況下&#xff0c;有如下代碼user.getAddress().getProvince();這種寫法&#xff0c;…

mysql百萬數據根據索引查詢_mysql創建多列索引查詢百萬表數據的性能優化經驗分享...

最近發現最代碼網站中的收到的評論&#xff0c;提到我的&#xff0c;心情被贊的查詢異常緩慢&#xff0c;通過nginx日志發現響應時間快的在5s&#xff0c;慢的有13s&#xff0c;終于忍無可忍花時間來解決了。執行explain之后的截圖如下&#xff1a;可以看到possible_keys中有很…

php用到的mysql語句_PHP中常用到的一些MySQL語句_php

在php開發中&#xff0c;經常會使用到mysql語句&#xff0c;下面就為您列舉了一些經常使用的MySQL語句&#xff0c;希望對您平時的學習和開發工作能起到些許的作用。MySQL語句顯示數據庫或表:show databases;//然后可以use database_name;show tables;MySQL語句更改表名:alter …

mysql執行計劃性能_MySQL SQL性能分析Explain執行計劃

一. 執行計劃返回信息詳解①. 執行計劃所含字段輸出列含義id查詢標識select_type查詢類型table查詢涉及的表partitions匹配到的分區信息type連接類型possible_keys可能選擇的索引key實際使用的索引key_len實際使用的索引的長度ref和索引進行比較的列rows需要被檢索的大致行數fi…

mysql定時作業_mysql 讓一個存儲過程定時作業的代碼(轉)

1、在mysql 中建立一個數據庫 test1語句&#xff1a;create database test12、創建表examinfocreate table examinfo(id int auto_increment not null,endtime datetime,primary key(id));3 插入數據&#xff1a;insert into examinfo values(‘1‘,‘2011-4-23 23:26:50‘);4 …

table虛線邊框_web前端工程師7天0基礎到精通(TABLE+CSS制作《互聯世紀網》)

項目七 項目實踐&#xff1a;TABLECSS制作《互聯世紀網》實踐目標1、 熟悉CSS屬性2、 熟練運用CSS屬性控制網頁樣式3、 熟悉網頁制作流程項目簡介&#xff1a;通過上一章節的學習&#xff0c;我們了解了CSS樣式能更加方便、有效地控制網頁結構和布局網頁元素&#xff0c;大大提…

mixamo骨骼_mixamo動作庫的模型和動作綁定控制器的方法-上集

1.首先從網站下載帶調好動作的文件fbx&#xff0c;我們將fbx場景文件轉換成c4d場景文件。沒轉換之前轉換之后選擇場次&#xff0c;在文件菜單里找到當前場次到新文檔&#xff01;2.我們將模型重置為Tpose方便后續操作&#xff0c;沒重置之前模型為k好的動作模式不能使用選中權重…

mysql 101_MySQL 調優/優化的 101 個建議!

原文&#xff1a;http://www.monitis.com/blog/101-tips-to-mysql-tuning-and-optimization/MySQL是一個強大的開源數據庫。隨著MySQL上的應用越來越多&#xff0c;MySQL逐漸遇到了瓶頸。這里提供 101 條優化 MySQL 的建議。有些技巧適合特定的安裝環境&#xff0c;但是思路是相…

數據安全:保護個人隱私和企業機密的關鍵

在當今數字化時代&#xff0c;數據已經成為了一種寶貴的資源。無論是個人還是企業&#xff0c;都離不開數據的支持。然而&#xff0c;隨著數據的不斷增長和廣泛應用&#xff0c;數據安全問題也日益突出。數據泄露、黑客攻擊、網絡詐騙等安全事件層出不窮&#xff0c;給個人和企…