高效 DOM 操作
减少重排重绘、批量更新、事件委托等 DOM 优化技巧
问题
如何高效操作 DOM?
解答
1. 减少 DOM 访问次数
// ❌ 差:多次访问 DOM
for (let i = 0; i < 100; i++) {
document.getElementById('list').innerHTML += `<li>${i}</li>`;
}
// ✅ 好:缓存 DOM 引用,一次性更新
const list = document.getElementById('list');
let html = '';
for (let i = 0; i < 100; i++) {
html += `<li>${i}</li>`;
}
list.innerHTML = html;
2. 使用 DocumentFragment
// 创建文档片段,避免多次重排
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const li = document.createElement('li');
li.textContent = `Item ${i}`;
fragment.appendChild(li); // 操作在内存中进行
}
// 只触发一次重排
document.getElementById('list').appendChild(fragment);
3. 批量修改样式
// ❌ 差:多次修改样式,触发多次重排
const el = document.getElementById('box');
el.style.width = '100px';
el.style.height = '100px';
el.style.margin = '10px';
// ✅ 好:使用 cssText 或 class 一次性修改
el.style.cssText = 'width: 100px; height: 100px; margin: 10px;';
// 或者切换 class
el.className = 'box-style';
4. 离线操作 DOM
// 将元素脱离文档流后再操作
const list = document.getElementById('list');
// 方法1:隐藏元素
list.style.display = 'none';
// 进行多次 DOM 操作...
for (let i = 0; i < 100; i++) {
const li = document.createElement('li');
list.appendChild(li);
}
list.style.display = 'c9s3v';
// 方法2:克隆节点
const clone = list.cloneNode(true);
// 在克隆节点上操作...
list.parentNode.replaceChild(clone, list);
5. 避免强制同步布局
// ❌ 差:读写交替,强制同步布局
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => {
const width = box.offsetWidth; // 读
box.style.width = width + 10 + 'px'; // 写
});
// ✅ 好:先批量读,再批量写
const widths = [];
boxes.forEach(box => {
widths.push(box.offsetWidth); // 批量读
});
boxes.forEach((box, i) => {
box.style.width = widths[i] + 10 + 'px'; // 批量写
});
6. 事件委托
// ❌ 差:给每个元素绑定事件
document.querySelectorAll('li').forEach(li => {
li.addEventListener('click', handleClick);
});
// ✅ 好:利用事件冒泡,在父元素上监听
document.getElementById('list').addEventListener('click', (e) => {
if (e.target.tagName === 'LI') {
handleClick(e);
}
});
7. 使用 requestAnimationFrame
// 将 DOM 操作放到下一帧执行,避免阻塞
function updateDOM() {
requestAnimationFrame(() => {
// DOM 操作在浏览器下次重绘前执行
element.style.transform = `translateX(${position}px)`;
});
}
关键点
- 减少访问次数:缓存 DOM 引用,避免重复查询
- 批量操作:使用 DocumentFragment 或字符串拼接,一次性插入
- 避免强制同步布局:读写分离,先批量读取再批量写入
- 事件委托:利用冒泡机制,减少事件监听器数量
- requestAnimationFrame:动画和频繁更新使用 rAF 优化
目录