DOM是浏览器解析HTML生成的实时树状对象结构,document为根节点;它动态同步页面,直接操作本身无错,但频繁触发重排重绘会导致性能瓶颈。

DOM 是什么?它不是 API,而是页面的实时映射
DOM(Document Object Model)是浏览器把 HTML 文档解析后生成的树状对象结构,document 就是它的根节点。它不是一份静态快照,而是与页面完全同步的“活对象”——你改 element.textContent,页面立刻重绘;你删 node,对应元素马上消失。
这意味着:直接操作 DOM 本身没有错,但频繁、低效地触发重排(reflow)和重绘(repaint)才是性能瓶颈的根源。
为什么 innerHTML += 是危险操作?
看似方便的拼接写法实际会强制浏览器反复销毁重建子树:
- 每次赋值都触发一次完整的 HTML 解析 + 节点重建
- 原有绑定的事件监听器全部丢失(
onclick、addEventListener都失效) - 输入框内容、滚动位置、焦点状态等用户态信息一并清空
正确做法是用文档片段或批量更新:
立即学习“Java免费学习笔记(深入)”;
const fragment = document.createDocumentFragment();
for (let i = 0; i < data.length; i++) {
const li = document.createElement('li');
li.textContent = data[i];
fragment.appendChild(li);
}
listElement.appendChild(fragment); // 仅一次真实 DOM 插入
textContent vs innerText:选错就踩坑
二者都设文本,但行为差异极大:
-
textContent:纯字符写入,不解析 HTML,保留所有空白符,读取时返回所有文本节点(含script、style内容) -
innerText:受 CSS 影响(如display: none元素内容不计入),会折叠空白,且在某些浏览器中触发重排
除非明确需要模拟用户可见文本(比如做可访问性摘要),否则一律用 textContent。写入富文本?用 insertAdjacentHTML() 替代 innerHTML,更可控:
el.insertAdjacentHTML('beforeend', 'Hello');
批量更新怎么避免强制同步布局?
只要读取了某些布局相关属性(如 offsetHeight、getBoundingClientRect()、scrollLeft),浏览器就会立刻计算当前样式并执行重排——如果前后夹着 DOM 修改,就形成“重排-修改-重排”恶性循环。
解决方法是把读操作和写操作分开:
- 先批量读取所有需要的尺寸/位置(缓存到变量)
- 再批量修改 DOM(插入、删除、class 切换等)
- 避免在循环里交替读写
现代方案可用 requestAnimationFrame() 或 ResizeObserver 延迟到下一帧,但最稳的仍是手动分离读写逻辑。
DOM 操作本身不慢,慢的是你没意识到浏览器什么时候被迫停下主线程去算布局。











