JavaScript性能優化:DOM操作優化實戰
一 重排與重繪的代價
問題場景
用戶點擊按鈕后,需要動態生成一個包含10,000個選項的下拉列表,但界面出現長達5秒的凍結。
錯誤代碼示例
function createList() {const ul = document.getElementById("myList");for(let i=0; i<10000; i++){const li = document.createElement("li");li.style.color = "red"; // 觸發樣式計算li.style.margin = "2px"; // 觸發布局li.textContent = `選項 ${i}`;ul.appendChild(li); // 每次循環都導致重排}
}
問題分析
- 每次循環都修改元素樣式 ? 觸發 強制同步布局(Forced Synchronous Layout)
- 直接操作真實DOM ? 累計觸發 10,000次重排
二 高效DOM操作三板斧
優化方案1:文檔碎片(DocumentFragment)
function createListOptimized() {const ul = document.getElementById("myList");const fragment = document.createDocumentFragment(); // 內存中的虛擬容器for(let i=0; i<10000; i++){const li = document.createElement("li");// 先完成所有屬性設置li.textContent = `選項 ${i}`;li.className = "prestyled-item"; // 通過CSS類批量設置樣式fragment.appendChild(li); // 不會觸發真實DOM操作}ul.appendChild(fragment); // 僅1次重排
}
優化方案2:樣式批量處理
/* 將樣式集中到CSS類 */
.prestyled-item {color: red;margin: 2px;
}
優化方案3:讀寫分離原則
// 錯誤:交替讀寫布局屬性
element.style.width = "100px";
const height = element.offsetHeight; // 觸發立即計算
element.style.height = height + "px";// 正確:先讀后寫
const height = element.offsetHeight; // 集中讀取
element.style.width = "100px"; // 集中寫入
element.style.height = height + "px";
三 事件監聽器的正確姿勢
內存泄漏陷阱
// 錯誤:為每個列表項綁定監聽器
document.querySelectorAll(".item").forEach(item => {item.addEventListener("click", handleClick);
});// 當需要移除元素時:
container.innerHTML = ""; // 監聽器仍然殘留在內存中!
優化方案:事件委托
// 正確:利用事件冒泡在父級監聽
document.getElementById("container").addEventListener("click", e => {if(e.target.classList.contains("item")) {handleClick(e);}
});
優勢對比:
方式 | 內存占用(1000個元素) | 初始化耗時 |
---|---|---|
單獨綁定 | 1000個監聽器 | 120ms |
事件委托 | 1個監聽器 | 15ms |
四 虛擬DOM的啟示
核心思想
手動實現簡化版
let vDOM = { type: 'ul', children: [] };// 更新函數
function updateDOM() {const realDOM = document.createElement(vDOM.type);vDOM.children.forEach(child => {const node = document.createElement(child.type);node.textContent = child.content;realDOM.appendChild(node);});// 批量替換document.getElementById("app").replaceChildren(realDOM);
}// 添加新項時:
vDOM.children.push({ type: 'li', content: '新項目' });
requestAnimationFrame(updateDOM); // 在下一次重繪時集中更新
本章重點總結
- 批量操作:使用文檔碎片減少DOM操作次數
- 樣式管理:用CSS類替代直接樣式操作
- 事件優化:委托模式節省內存
- 更新策略:集中修改減少重排次數