1. 程式人生 > >CSS中的重繪和迴流

CSS中的重繪和迴流

一.瀏覽器的渲染過程:

1.渲染圖:

在這裡插入圖片描述

2.瀏覽器渲染過程:

(1)解析HTML,生成DOM樹,解析CSS,生成CSSOM樹

(2)將DOM樹和CSSOM樹結合,生成渲染樹(Render Tree)

(3)Layout(迴流):根據生成的渲染樹,進行迴流(Layout),得到節點的幾何資訊(位置,大小)

(4)Painting(重繪):根據渲染樹以及迴流得到的幾何資訊,得到節點的絕對畫素

(5)Display:將像素髮送給GPU,展示在頁面上

二.生成渲染樹:

1.生成圖:

在這裡插入圖片描述

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

(1)從DOM樹的根節點開始遍歷每個可見節點

(2)對於每個可見的節點,找到CSSOM樹中對應的規則,並應用它們

(3)根據每個可見節點以及其對應的樣式,組合生成渲染樹。

3.不可見的節點包括:

(1)一些不會渲染輸出的節點,比如script、meta、link等

(2)一些通過css進行隱藏的節點。比如display:none。注意,利用visibility和opacity隱藏的節點,還是會顯示在渲染樹上的。只有display:none的節點才不會顯示在渲染樹上

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

三.迴流:

1.迴流的概念:

將可見DOM節點以及它對應的樣式結合起來,可是我們還需要計算它們在裝置視口(viewport)內的確切位置和大小,這個計算的階段就是迴流

2.程式碼示例:

<!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>

3.程式碼分析:

(1)第一個div將節點的顯示尺寸設定為視口寬度的50%,第二個div將其尺寸設定為父節點的50%。而在迴流這個階段,我們就需要根據視口具體的寬度,將其轉為實際的畫素值

(2)效果圖:

在這裡插入圖片描述

四.重繪:

1.重繪概念:

我們通過構造渲染樹和迴流階段,我們知道了哪些節點是可見的,以及可見節點的樣式和具體的幾何資訊(位置、大小),那麼我們就可以將渲染樹的每個節點都轉換為螢幕上的實際畫素,這個階段就叫做重繪節點

2.何時發生迴流重繪:

(1)迴流這一階段主要是計算節點的位置和幾何資訊,那麼當頁面佈局和幾何資訊發生變化的時候,就需要回流

比如以下情況:

a.新增或刪除可見的DOM元素

b.元素的位置發生變化

c.元素的尺寸發生變化(包括外邊距、內邊框、邊框大小、高度和寬度等)

d.內容發生變化,比如文字變化或圖片被另一個不同尺寸的圖片所替代

e.頁面一開始渲染的時候(這肯定避免不了)

f.瀏覽器的視窗尺寸變化(因為迴流是根據視口的大小來計算元素的位置和大小的)

(2)迴流一定會觸發重繪,而重繪不一定會迴流

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

五.瀏覽器的優化機制:

1.優化機制:

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

(2)當你獲取佈局資訊的操作的時候,會強制佇列重新整理,比如當你訪問以下屬性或者使用以下方法:

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

(3)以上屬性和方法都需要返回最新的佈局資訊,因此瀏覽器不得不清空佇列,觸發迴流重繪來返回正確的值。因此,我們在修改樣式的時候,最好避免使用上面列出的屬性,他們都會重新整理渲染佇列。如果要使用它們,最好將值快取起來

六.減少迴流和重繪:

1.最小化重繪和重排:

(1)由於重繪和重排可能代價比較昂貴,因此最好就是可以減少它的發生次數。為了減少發生次數,我們可以合併多次對DOM和樣式的修改,然後一次處理掉

(2)程式碼例子:

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

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

(4)因此,我們可以合併所有的改變然後依次處理,比如我們可以採取以下的方式:

a、使用cssText

const  el = document.getElementById('test');
el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px;';

b、修改CSS的class

const  el = document.getElementById('test');
el.className += ' active';

2.批量修改DOM:

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

a.使元素脫離文件流

b.對其進行多次修改

c.將元素帶回到文件中

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

(2)有三種方式可以讓DOM脫離文件流:

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

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

c.將原始元素拷貝到一個脫離文件的節點中,修改節點後,再替換原始的元素

(3)示例程式碼:

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);

(4)如果我們直接這樣執行的話,由於每次迴圈都會插入一個新的節點,會導致瀏覽器迴流一次。

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

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

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

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';

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

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

c.將原始元素拷貝到一個脫離文件的節點中,修改節點後,再替換原始的元素:

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

3.避免觸發同步佈局事件

4.對於複雜動畫效果,使用絕對定位讓其脫離文件流:

對於複雜動畫效果,由於會經常的引起迴流重繪,因此,我們可以使用絕對定位,讓它脫離文件流。否則會引起父元素以及後續元素頻繁的迴流

5.css3硬體加速(GPU加速):

(1)使用css3硬體加速,可以讓transform、opacity、filters這些動畫不會引起迴流重繪 。但是對於動畫的其它屬性,比如background-color這些,還是會引起迴流重繪的,不過它還是可以提升這些動畫的效能

(2)如何使用:

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

transform
opacity
filters
Will-change

(3)使用css3硬體加速,可以讓transform、opacity、filters這些動畫不會引起迴流重繪。

對於動畫的其它屬性,比如background-color這些,還是會引起迴流重繪的,不過它還是可以提升這些動畫的效能

(4)如果你為太多元素使用css3硬體加速,會導致記憶體佔用較大,會有效能問題。

在GPU渲染字型會導致抗鋸齒無效。這是因為GPU和CPU的演算法不同。因此如果你不在動畫結束的時候關閉硬體加速,會產生字型模糊