/** * Stage 04-perf:真实 DOM 操作性能实证(耗时对比版) * 动态调整数据量,复杂节点,三种更新策略耗时一目了然 */ const listEl = document.getElementById("list"); const statsEl = document.getElementById("stats"); const compareDisplay = document.getElementById("compareDisplay"); const rangeN = document.getElementById("rangeN"); const nValueSpan = document.getElementById("nValue"); // ---------- 当前数据量(与滑块同步)---------- let N = 10000; let items = []; // 记录最近一次各方案耗时(ms),用于页面展示 const lastResult = { innerHTML: "-", createElement: "-", exact: "-", }; // ---------- 工具函数:生成初始数据 ---------- function generateItems(count) { return Array.from({ length: count }, (_, i) => ({ id: i, title: `Item ${i}`, desc: `Description for item ${i}`, tag: i % 3 === 0 ? "active" : "inactive", })); } // ---------- 复杂节点构造 ---------- /** * 生成带子元素的
  • ,模拟真实 UI 片段 * @param {Object} item * @returns {HTMLLIElement} */ function createComplexItem(item) { const li = document.createElement("li"); li.className = "list-item"; const avatar = document.createElement("div"); avatar.className = "avatar"; avatar.textContent = item.id % 100; const content = document.createElement("div"); content.className = "content"; const title = document.createElement("h4"); title.textContent = item.title; const desc = document.createElement("p"); desc.textContent = item.desc; const tag = document.createElement("span"); tag.className = "tag " + item.tag; tag.textContent = item.tag; content.appendChild(title); content.appendChild(desc); content.appendChild(tag); li.appendChild(avatar); li.appendChild(content); return li; } /** * 构建整个列表的 HTML 字符串(innerHTML 方案) * @param {Object[]} data * @returns {string} */ function buildHTML(data) { let html = ""; for (let i = 0; i < data.length; i++) { const item = data[i]; html += `
  • ${item.id % 100}

    ${item.title}

    ${item.desc}

    ${item.tag}
  • `; } return html; } /** * 全量 createElement 重建列表 * @param {Object[]} data */ function rebuildWithCreateElement(data) { listEl.innerHTML = ""; for (let i = 0; i < data.length; i++) { listEl.appendChild(createComplexItem(data[i])); } } /** * 精确更新单个项目的内容(只改文字) * @param {number} index * @param {Object} newItem */ function updateOneItemExact(index, newItem) { const li = listEl.children[index]; if (!li) return; const h4 = li.querySelector("h4"); if (h4) h4.textContent = newItem.title; const p = li.querySelector("p"); if (p) p.textContent = newItem.desc; const span = li.querySelector("span"); if (span) { span.textContent = newItem.tag; span.className = "tag " + newItem.tag; } } // ---------- 更新页面上的耗时对比显示 ---------- function updateCompareDisplay() { compareDisplay.innerHTML = ` 最近耗时对比(N = ${N})
    innerHTML 全量:${lastResult.innerHTML}ms | createElement 全量:${lastResult.createElement}ms | 精确更新:${lastResult.exact}ms `; } // ---------- 重置列表到当前 N ---------- function resetList() { items = generateItems(N); console.time(`reset (${N} 项 createElement)`); rebuildWithCreateElement(items); console.timeEnd(`reset (${N} 项 createElement)`); statsEl.textContent = `列表已重置(${N} 项)`; // 清空耗时记录 lastResult.innerHTML = "-"; lastResult.createElement = "-"; lastResult.exact = "-"; updateCompareDisplay(); } // ---------- 滑块事件 ---------- // 滑块仅更新显示数值,不自动重建 rangeN.addEventListener("input", (e) => { N = parseInt(e.target.value, 10); nValueSpan.textContent = N; }); // 点击“应用”按钮才重建列表 document.getElementById("btnApplyN").addEventListener("click", () => { resetList(); }); // ---------- 按钮事件 ---------- document.getElementById("btnReset").addEventListener("click", resetList); document.getElementById("btnFullInnerHTML").addEventListener("click", () => { const newItems = items.map((item, i) => i === 5000 ? { ...item, title: "CHANGED" } : item, ); console.time(`innerHTML 全量 (${N} 项)`); listEl.innerHTML = buildHTML(newItems); console.timeEnd(`innerHTML 全量 (${N} 项)`); // 从控制台取不到精确时间,用 performance.now 手动记录 const start = performance.now(); listEl.innerHTML = buildHTML(newItems); const end = performance.now(); lastResult.innerHTML = (end - start).toFixed(2); items = newItems; statsEl.textContent = "innerHTML 更新完成"; updateCompareDisplay(); }); document.getElementById("btnFullCreate").addEventListener("click", () => { const newItems = items.map((item, i) => i === 5000 ? { ...item, title: "CHANGED" } : item, ); const start = performance.now(); rebuildWithCreateElement(newItems); const end = performance.now(); lastResult.createElement = (end - start).toFixed(2); console.log(`createElement 全量耗时: ${lastResult.createElement}ms`); items = newItems; statsEl.textContent = "createElement 全量完成"; updateCompareDisplay(); }); document.getElementById("btnExact").addEventListener("click", () => { const targetIndex = Math.min(5000, N - 1); const newItems = [...items]; newItems[targetIndex] = { ...newItems[targetIndex], title: "CHANGED" }; const start = performance.now(); updateOneItemExact(targetIndex, newItems[targetIndex]); const end = performance.now(); lastResult.exact = (end - start).toFixed(2); console.log(`精确更新耗时: ${lastResult.exact}ms`); items = newItems; statsEl.textContent = "精确更新完成"; updateCompareDisplay(); }); // ---------- 启动 ---------- resetList();