
本文详解 JavaScript 动态渲染对象数组时常见的“重复渲染”问题——根源在于每次调用 render() 都重新遍历整个数组并追加全部元素,导致已存在的 DOM 节点不断累积。解决方案是只渲染新增部分,并合理管理渲染状态。
本文详解 javascript 动态渲染对象数组时常见的“重复渲染”问题——根源在于每次调用 render() 都重新遍历整个数组并追加全部元素,导致已存在的 dom 节点不断累积。解决方案是只渲染新增部分,并合理管理渲染状态。
在构建动态列表(如自由职业者展示页)时,一个典型误区是:将“重绘逻辑”与“增量更新逻辑”混为一谈。你当前的 render() 函数每次都会遍历 initial 数组的全部元素(包括最初那两位),并为每个对象创建新的 DOM 节点,再 appendChild 到 .container 中。而 addFreelancer() 每次调用 render() 时,并未清空容器,因此历史节点持续保留,新节点不断叠加——这就是你看到 “Alice” 和 “Bob” 反复出现的根本原因。
✅ 正确做法应满足两个核心原则:
- 渲染函数只负责“追加新增项”,不重复处理已有内容;
- 状态变量记录已渲染数量,确保每次仅处理增量部分。
以下是优化后的关键实现:
let renderedCount = 0; // 记录已渲染的元素数量,初始为 0
function render() {
const container = document.querySelector(".container");
// 仅从上一次渲染结束位置开始,渲染新增的元素
for (let i = renderedCount; i <p>同时,请注意 <code>addFreelancer()</code> 中调用 <code>averageStartingPrice()</code> 后并未更新页面上的平均价格显示——你需要<strong>显式更新 DOM 元素内容</strong>:</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill3458" title="html-ppt-to-pdf"><img
src="https://img.php.cn/upload/skill/000/000/081/178956546773641.jpg" alt="html-ppt-to-pdf" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill3458" title="html-ppt-to-pdf" class="overflowclass">html-ppt-to-pdf</a>
<p class="overflowclass">将使用 `<section class="slide">` 约定的 HTML 幻灯片转换为高保真、矢量文本 PDF(使用 Playwright + Chromium 原生 PDF 功能)。</p>
</div>
<a rel="nofollow" href="/xiazai/skill3458" title="html-ppt-to-pdf" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><pre class="brush:php;toolbar:false;">function averageStartingPrice() {
if (initial.length === 0) return 0;
const total = initial.reduce((sum, f) => sum + f.price, 0);
return Number((total / initial.length).toFixed(2));
}
// 在 addFreelancer() 或 render() 结尾处同步更新页面
function updateAverageDisplay() {
const avgElement = document.querySelector(".avg");
if (avgElement) {
avgElement.innerText = `The average starting price is $${averageStartingPrice()}`;
}
}
// 修改 addFreelancer()
function addFreelancer() {
if (initial.length >= maxLancers) return;
const freelancer = extraFreelancers[
Math.floor(Math.random() * extraFreelancers.length)
];
initial.push(freelancer);
render();
updateAverageDisplay(); // ✅ 关键:刷新平均价文本
if (initial.length >= maxLancers) {
clearInterval(addFreelancerInterval);
}
}⚠️ 额外建议(提升健壮性):
-
首次渲染前清空容器(可选,若需支持重置):
container.innerHTML = ""—— 但本例中因采用增量策略,通常无需此步。 -
避免全局污染:将
renderedCount、initial等封装进 IIFE 或模块作用域。 -
防抖/节流考虑:若未来扩展为高频更新(如实时竞价),应限制
render()执行频率。 -
使用
textContent替代innerText:更安全(防止 XSS)、性能略优,且语义更准确。
总结:动态列表渲染 ≠ 每次全量重绘。通过维护一个轻量级的渲染游标(renderedCount),配合只追加、不清理的策略,即可高效、无重复地实现数据驱动的 UI 增量更新。这是前端开发中「响应式列表」的基础范式,也适用于 React/Vue 等框架背后的 reconciler 思想雏形。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










