Skip to main content

DOM

DOM (文档对象模型) 是 JS 操作网页的接口。面试中最常考的不是具体的 DOM API 怎么拼写,而是DOM 操作的性能瓶颈(重绘与回流)以及如何通过各种手段(如 DocumentFragment、虚拟 DOM)来减少对真实 DOM 的频繁操作。

节点操作核心 API

  • 查找节点querySelector / querySelectorAll (返回静态 NodeList) vs getElementsByClassName (返回动态 HTMLCollection,随 DOM 变化而实时更新)。
  • 创建节点document.createElementdocument.createTextNode
  • 修改节点appendChildinsertBeforeremoveChildreplaceChild
  • 属性操作setAttributegetAttribute

重绘(Repaint)与回流/重排(Reflow)

这是前端性能优化的绝对核心,逢考必问!

  • 回流 (Reflow):当 DOM 的变化影响了元素的几何信息(如宽度、高度、位置、隐藏/显示),浏览器需要重新计算 DOM 树中受影响节点的几何属性,并重新排列它们的位置。极其消耗性能
    • 触发条件:添加/删除可见 DOM、改变元素尺寸/位置、窗口 resize。
    • 致命陷阱:当你通过 JS 获取某些布局属性时(如 offsetWidthclientHeightscrollTopgetComputedStyle),浏览器为了给你返回最精确的值,会强制清空渲染队列,立即触发一次回流
  • 重绘 (Repaint):当一个元素的外观发生改变,但没有改变布局,浏览器把新的外观绘制出来的过程。
    • 触发条件:改变 colorbackground-colorvisibility 等。
  • 关系回流必将引起重绘,但重绘不一定会引起回流。

如何优化 DOM 操作性能?

面对高频 DOM 操作,我们可以采取以下策略来压榨性能:

  1. 合并样式修改:避免一行行修改 style,而是通过修改 classNamecssText 一次性更新。
  2. 批量 DOM 操作 (离线操作)
    • 使用 DocumentFragment(文档片段)。它存在于内存中,不在 DOM 树中。将成百上千个新节点插入到 fragment 中,最后一次性 appendChild 到页面上,只触发一次回流
    • 先将元素 display: none(触发一次回流),进行大量修改后,再 display: block(触发第二次回流),避免了中间成百上千次回流。
  3. 缓存布局信息:避免在循环中频繁读取 offsetWidth 等会引发强制回流的属性,用一个变量把它缓存起来。
  4. CSS3 硬件加速:对于动画,尽量使用 transformopacity。它们不会触发重绘和回流,而是直接交由 GPU 在合成线程(Compositor Thread)处理,性能极高。

虚拟 DOM 真的比原生 DOM 快吗?

面试官经常会抛出这个“陷阱题”来考察你的独立思考能力。很多初学者会被洗脑,认为“因为操作原生 DOM 慢,所以 Vue/React 的虚拟 DOM 快”。这是绝对错误的!

没有任何框架能比纯手工优化的原生 DOM 操作更快。

  1. 首次渲染
    • 原生:直接创建 DOM 并插入。
    • 虚拟 DOM:先创建虚拟 DOM 对象 -> 通过框架转化为原生 DOM -> 插入。明显虚拟 DOM 更慢,因为它多了一层 JS 对象的计算。
  2. 微小局部更新
    • 原生:精确定位到那个节点,直接修改文本。
    • 虚拟 DOM:重新生成整个组件的虚拟 DOM 树 -> Diff 算法对比新旧树找出差异 -> 修改真实的 DOM。这里 Diff 算法的开销是不容忽视的。
  3. 复杂列表更新(比如打乱顺序)
    • 原生:如果没有深厚的功底,很容易写出全部卸载再重新挂载的灾难级代码(引发剧烈回流)。
    • 虚拟 DOM:通过 Diff 算法和 Key 机制,计算出最小的移动/复用路径,最后统一打补丁(Patch)。

总结: “虚拟 DOM 并不是为了追求极致的性能而诞生的。它的真正价值在于‘开发体验’与‘性能’之间的绝佳平衡。 它为我们提供了一种声明式的 UI 编程方式,让我们不用再手动去追踪状态和精细化操作 DOM;同时,它在底层通过 Diff 算法和批量更新(Batching)机制,保证了即使在极其复杂的交互下,我们的代码依然能保持在性能的及格线(下限)之上。此外,虚拟 DOM 的抽象层也为跨端(如 React Native、Weex)提供了可能。”