JavaScript 操作DOM效能優化

admin發表於2018-12-27

JavaScript主要功能之一就是用來操作DOM,但操作DOM是非常昂貴的行為。

在初學階段,達成目標是主要目標,隨著技術的進步,會自然而然的關注程式碼的效能。

程式碼的效能對於小型的專案影響不是太大,但是對於大型專案或者操作量較高的專案則有明顯的影響。

下面介紹一下常見的提高效能的方式。

一.遍歷元素集合:

應用中,可能需要使用指定方法獲取元素物件集合,然後對其進行遍歷。

程式碼例項如下:

[HTML] 純文字檢視 複製程式碼執行程式碼
<!DOCTYPE html>
<html>
<head>
<meta charset=" utf-8">
<meta name="author" content="http://www.softwhy.com/" />
<title>螞蟻部落</title>
<style type="text/css">
#box li {
  width: 350px;
  height: 25px;
  line-height: 25px;
  font-size: 12px;
}
</style>
<script>
addEventListener("DOMContentLoaded",function(){
  let lis=document.getElementsByTagName("li");
  for(let index=0;index<lis.length;index++){
    lis[index].style.color="blue";
  }
},false);
</script>
</head>
<body>
  <ul id="box">
    <li>螞蟻部落一</li>
    <li>螞蟻部落二</li>
    <li>螞蟻部落三</li>
    <li>螞蟻部落四</li>
    <li>螞蟻部落五</li>
    <li>螞蟻部落六</li>
  </ul>
</body>
</html>

程式碼分析如下:

(1).通過getElementsByTagName方法獲取li元素集合。

(2).此集合是動態的,也就是它能夠自動實時感知li元素的增加或者減少。

(3).然後通過for迴圈遍歷每一個li元素,再將其字型元素設定為藍色。

上述程式碼並不是最優方式,由於集合是動態的,所以lis.length獲取集合中的元素數量效能較差。

那麼可以將元素集合的數量事先快取起來,而不是每迴圈一次獲取一次,程式碼修改如下:

[JavaScript] 純文字檢視 複製程式碼
addEventListener("DOMContentLoaded",function(){
  let lis=document.getElementsByTagName("li");
  let len=lis.length;
  for(let index=0;index<len;index++){
    lis[index].style.color="blue";
  }
},false);

二.將多次樣式操作合併為一次:

實際應用中,可能會使用如下方式設定元素的樣式,程式碼片段如下:

[JavaScript] 純文字檢視 複製程式碼
let odiv = document.getElementById("ant");
odiv.style.color = "red";
odiv.style.background = "blue";
odiv.style.width = "200px";
odiv.style.height = "100px";

上述程式碼可以設定div元素的字型顏色、背景顏色和元素尺寸。

一切看起來很正常,但是效能比較低下,如果操作龐大,效能會有明顯下降。

因為每一次使用style設定,都會引發頁面的重繪或者重排,程式碼修改如下:

CSS程式碼如下:

[CSS] 純文字檢視 複製程式碼
#ant {
  background: blue;
  color: red;
  width:200px;
  height: 200px;
}

JavaScript程式碼如下:

[JavaScript] 純文字檢視 複製程式碼
let odiv = document.getElementById("ant");
odiv.className = "ant";

使用上述程式碼,重繪和重排只有一次,可以極大提高想能。

重繪和重排是非常消耗效能的行為,具體參閱JavaScript 重繪和重排簡介一章節。

三.經常需要重排的元素設定為絕對定位:

有些元素可能需要經常重排,那麼可以將它們設定為絕對定位。

這樣它們就可以脫離文件流,即便它們發生重排也不會影響其他元素,縮小了重排的範圍。

四.使用fragment暫存節點:

假設我們需要依次建立若干個li元素,然後將其追加到ul中。

ul中追加li元素是一個重繪或者重排操作,很消耗效能,看如下程式碼片段:

[HTML] 純文字檢視 複製程式碼執行程式碼
<!DOCTYPE html>
<html>
<head>
<meta charset=" utf-8">
<meta name="author" content="http://www.softwhy.com/" />
<title>螞蟻部落</title>
<script>
addEventListener("DOMContentLoaded",function(){
  let arr=["螞蟻部落一","螞蟻部落二","螞蟻部落三","螞蟻部落四","螞蟻部落五"];
  let oul=document.getElementById("box");
  for(let index=0;index<5;index++){
    let li=document.createElement("li");
    let oTxt=document.createTextNode("螞蟻部落"+arr[index]);
    li.appendChild(oTxt);
    oul.appendChild(li);
  }
},false);
</script>
</head>
<body>
<ul id="box"></ul>
</body>
</html>

上述程式碼通過for迴圈方式依次建立li元素,然後再依次將li元素追加到ul元素中。

雖然實現了預期效果,但是效能非常不好,因為進行了多次追加操作,進行了多次重繪重排。

所以可以將每一次附加操作暫存起來,然後一次性追加到ul中,進行一次重繪重排操作即可。

程式碼修改如下:

[HTML] 純文字檢視 複製程式碼執行程式碼
<!DOCTYPE html>
<html>
<head>
<meta charset=" utf-8">
<meta name="author" content="http://www.softwhy.com/" />
<title>螞蟻部落</title>
<script>
addEventListener("DOMContentLoaded",function(){
  let arr=["螞蟻部落一","螞蟻部落二","螞蟻部落三","螞蟻部落四","螞蟻部落五"];
  let oFragment=document.createDocumentFragment();
  let oul=document.getElementById("box");
  for(let index=0;index<5;index++){
    let li=document.createElement("li");
    let oTxt=document.createTextNode("螞蟻部落"+arr[index]);
    li.appendChild(oTxt);
    oFragment.appendChild(li);
  }
  oul.appendChild(oFragment);
},false);
</script>
</head>
<body>
<ul id="box"></ul>
</body>
</html>

程式碼實現了相同的效果,但是效能更佳,分析如下:

(1).利用document.createDocumentFragment方法建立一個暫存器。

(2).然後將附加操作放入這個暫存器。

(3).最後將暫存器附加到ul中,事實就是將所有li元素一次性附加到ul。

關於document.createDocumentFragment方法參閱document.createDocumentFragment()一章節。

五.巧用display:none元素:

隱藏元素不會影響不在渲染樹中,對它的任何操作都不會引發重拍或者重繪操作。

所以要對元素進行比較複雜的各種操作時,如果允許的話,可以將其暫時隱藏起來。

操作完畢,再將其顯示出來,這樣只會產生一次重繪和重排操作。

相關文章