如何优化 JavaScript 中的 DOM 操作?

推荐答案

优化 JavaScript 中的 DOM 操作主要围绕以下几个方面:

  • 减少 DOM 操作次数:
    • 使用 DocumentFragment: 将多次 DOM 操作集中到一个 DocumentFragment 中,最后一次性添加到 DOM 中,减少浏览器重排和重绘。
    • 字符串拼接: 对于大量 HTML 内容的插入,使用字符串拼接或模板字面量构建 HTML 字符串,然后一次性 innerHTMLinsertAdjacentHTML,而不是多次 createElementappendChild
  • 避免频繁访问 DOM 属性:
    • 缓存 DOM 元素: 将经常使用的 DOM 元素引用存储在变量中,避免重复查询。
    • 缓存样式值: 如果需要多次读取元素的样式值,可以将样式值缓存到变量中。
  • 优化事件处理:
    • 事件委托(事件冒泡): 将事件监听器添加到父元素,而不是每个子元素,减少事件监听器的数量,提高性能。
    • 节流和防抖: 对于频繁触发的事件,如 scrollresizemousemove,使用节流或防抖技术来限制事件处理函数的执行频率。
  • 选择器优化:
    • 使用 ID 选择器: document.getElementById() 是最快的选择器。
    • 使用 querySelectorquerySelectorAll 时,尽量精确匹配: 避免使用过于宽泛的选择器,如 document.querySelector('div')
    • 利用上下文查询: 在已获取的 DOM 元素上使用 .querySelector().querySelectorAll() 搜索子元素,减少全局搜索范围。
  • 批量修改样式:
    • 使用 classNameclassList 修改样式: 而不是直接修改 style 属性,因为 classNameclassList 可以一次性修改多个样式。
    • 使用 CSS 动画和过渡: 对于简单的动画效果,使用 CSS 动画和过渡而不是 JavaScript 操作 DOM 的方式,减少 JavaScript 的计算和 DOM 操作。

本题详细解读

DOM 操作的性能瓶颈主要在于浏览器需要频繁地进行重排(Reflow)和重绘(Repaint)。每一次 DOM 结构或样式的改变都可能触发浏览器重新计算布局和绘制页面。

1. 减少 DOM 操作次数

  • DocumentFragment: DocumentFragment 是一个轻量级的 DOM 容器,它不会被渲染到页面上。我们可以先将所有的 DOM 操作都附加到 DocumentFragment 上,最后再将 DocumentFragment 添加到文档中,这样就只进行了一次 DOM 更新,大大减少了重绘和重排的次数。

  • 字符串拼接/模板字面量: 构建 HTML 字符串然后使用 innerHTML 或者 insertAdjacentHTML 一次性插入 DOM,通常比多次调用 createElementappendChild 快,尤其在大量元素需要添加时。

    -- -------------------- ---- -------
      --- ---- - ---
      --- ---- - - -- - - ---- ---- -
        ---- -- --------- --- - ---------
      -
      ----------------------------------------- - -----
    
       -- ---- ------------------
       ----- ------------- - -------------------------------
       ---------------------------------------------------

2. 避免频繁访问 DOM 属性

  • 缓存 DOM 元素: 如果你多次使用同一个 DOM 元素,应先将其引用存储在一个变量中,避免重复使用 getElementByIdquerySelector 等方法进行 DOM 查询。

  • 缓存样式值: 频繁读取元素的样式也会导致性能问题。如果需要多次读取元素的样式值,可以将样式值缓存到变量中。

3. 优化事件处理

  • 事件委托: 当有大量子元素需要添加事件监听时,将事件监听器添加到父元素上,利用事件冒泡机制来处理子元素的事件。这可以显著减少事件监听器的数量。

  • 节流和防抖: 对于如 scroll, resize, mousemove 等频繁触发的事件,可以使用节流或防抖来限制事件处理函数的执行频率,避免频繁的 DOM 操作。

    -- -------------------- ---- -------
    -- --
    -------- -------------- ------ -
        --- -------- - --
        ------ ----------------- -
          ----- --- - -----------
          -- ---- - -------- -- ------ -
            -------- - ----
            ---------------- ------
          -
        -
    -
    --------------------------------- ------------------- -
        -- ------
    -- -----
    
    -- --
    -------- -------------- ------ -
        --- ----------
        ------ ----------------- -
            ------------------------
            --------- - ------------- -- -
              ---------------- ------
            -- ------
        -
    -
    --------------------------------- ------------------- -
        -- ----------
    -- -----

4. 选择器优化

  • getElementByIdquerySelectorquerySelectorAll 快,因为它直接使用 ID 获取元素,速度最快。

  • querySelectorquerySelectorAll 应该尽量精确匹配,避免使用宽泛的选择器,减少浏览器的遍历范围。

  • 在已获取的 DOM 元素上调用 .querySelector().querySelectorAll() 而不是在 document 上调用,可以进一步减少搜索范围。

5. 批量修改样式

  • 使用 classNameclassList 修改样式,比直接修改 style 属性效率更高,因为前者可以一次性修改多个样式,而后者每次修改都会触发重排和重绘。

  • 对于简单的动画效果,优先使用 CSS 动画和过渡,浏览器对 CSS 动画的优化比 JavaScript 操作 DOM 的方式好。

纠错
反馈