MST

星途 面试题库

面试题:JavaScript 中大量动态添加和删除 DOM 元素的性能优化策略

假设在一个 Web 应用中,需要频繁地大量动态添加和删除 DOM 元素,可能会导致性能问题。请阐述一系列优化策略,包括但不限于事件委托、文档片段的使用、虚拟 DOM 概念的运用等,并结合代码示例说明如何有效提升性能。
10.7万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

1. 事件委托

  • 原理:将事件绑定到父元素,利用事件冒泡机制,让父元素统一处理子元素的事件。这样可以减少事件绑定的数量,提高性能。
  • 示例代码
<ul id="parent">
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>
<script>
const parent = document.getElementById('parent');
parent.addEventListener('click', function(event) {
  if (event.target.tagName === 'LI') {
    console.log('Clicked on:', event.target.textContent);
  }
});
</script>

2. 文档片段(DocumentFragment)

  • 原理:文档片段是一个轻量级的 DOM 容器,它存在于内存中,不在文档树里。对文档片段进行操作不会引起页面回流和重绘,操作完成后再将文档片段添加到文档树中,这样只会触发一次回流和重绘。
  • 示例代码
<div id="container"></div>
<script>
const container = document.getElementById('container');
const fragment = document.createDocumentFragment();
for (let i = 0; i < 10; i++) {
  const div = document.createElement('div');
  div.textContent = `Item ${i}`;
  fragment.appendChild(div);
}
container.appendChild(fragment);
</script>

3. 虚拟 DOM

  • 原理:虚拟 DOM 是一个用 JavaScript 对象来描述真实 DOM 的数据结构。当数据发生变化时,会生成新的虚拟 DOM,然后与旧的虚拟 DOM 进行对比(diff 算法),计算出最小的 DOM 变化,最后将这些变化应用到真实 DOM 上,从而减少直接操作真实 DOM 的性能开销。
  • 示例代码(使用 React 来简单示意,React 内部使用虚拟 DOM)
import React, { useState } from'react';

function App() {
  const [items, setItems] = useState([]);
  const addItem = () => {
    setItems([...items, `New Item ${items.length + 1}`]);
  };
  const removeItem = () => {
    if (items.length > 0) {
      setItems(items.slice(0, -1));
    }
  };
  return (
    <div>
      <button onClick={addItem}>Add Item</button>
      <button onClick={removeItem}>Remove Item</button>
      <ul>
        {items.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

export default App;

4. 批量操作

  • 原理:将多次 DOM 操作合并为一次,减少回流和重绘次数。
  • 示例代码
<div id="element1"></div>
<div id="element2"></div>
<script>
const element1 = document.getElementById('element1');
const element2 = document.getElementById('element2');
// 开始批量操作
element1.style.display = 'none';
element2.style.color ='red';
// 批量操作结束,此时只触发一次回流和重绘
</script>

5. 节流与防抖

  • 原理
    • 节流:规定在一个单位时间内,只能触发一次函数。如果在这个单位时间内触发多次函数,只有一次生效。
    • 防抖:在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时。
  • 示例代码(节流)
<button id="throttleButton">Click Me</button>
<script>
function throttle(func, delay) {
  let timer = null;
  return function() {
    if (!timer) {
      func.apply(this, arguments);
      timer = setTimeout(() => {
        timer = null;
      }, delay);
    }
  };
}
const throttleButton = document.getElementById('throttleButton');
const handleClick = function() {
  console.log('Button clicked');
};
throttleButton.addEventListener('click', throttle(handleClick, 1000));
</script>
  • 示例代码(防抖)
<input type="text" id="searchInput" placeholder="Search...">
<script>
function debounce(func, delay) {
  let timer = null;
  return function() {
    clearTimeout(timer);
    timer = setTimeout(() => {
      func.apply(this, arguments);
    }, delay);
  };
}
const searchInput = document.getElementById('searchInput');
const handleSearch = function() {
  console.log('Searching:', searchInput.value);
};
searchInput.addEventListener('input', debounce(handleSearch, 500));
</script>