添加stage04-perf用于真实DOM操作性能对比,包含三个不同的更新策略: - innerHTML全量更新 - createElement全量重建 - 精确单节点更新 添加stage04用于模拟异步接口数据获取后的渲染演示 添加stage05实现虚拟DOM基础功能,提供VNode对象描述DOM树结构和递 归渲染函数
211 lines
6.0 KiB
JavaScript
211 lines
6.0 KiB
JavaScript
/**
|
||
* 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",
|
||
}));
|
||
}
|
||
|
||
// ---------- 复杂节点构造 ----------
|
||
/**
|
||
* 生成带子元素的 <li>,模拟真实 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 += `<li class="list-item">
|
||
<div class="avatar">${item.id % 100}</div>
|
||
<div class="content">
|
||
<h4>${item.title}</h4>
|
||
<p>${item.desc}</p>
|
||
<span class="tag ${item.tag}">${item.tag}</span>
|
||
</div>
|
||
</li>`;
|
||
}
|
||
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})<br>
|
||
innerHTML 全量:<b>${lastResult.innerHTML}ms</b> |
|
||
createElement 全量:<b>${lastResult.createElement}ms</b> |
|
||
精确更新:<b>${lastResult.exact}ms</b>
|
||
`;
|
||
}
|
||
|
||
// ---------- 重置列表到当前 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();
|