删掉多余div能明显变快,因为DOM节点数量直接影响渲染树构建、布局计算和绘制开销,冗余嵌套显著增加主线程负担。

为什么删掉多余 div 就能明显变快
DOM 节点数量直接决定浏览器构建渲染树、计算布局和绘制的开销。一个 <div> 嵌套一层,就多一个节点;50 个列表项若每个都套 3 层 <div>,实际 DOM 节点数可能超 200 —— 这不是“看起来卡”,而是主线程被持续占用,连滚动事件都来不及响应。
真实场景中常见冗余结构:<div class="item"><div><div><span>内容</span></div></div></div>。语义上只需一个 <li> 或带 role 的 <div role="listitem"> 即可。
- 优先用语义化标签:
<ul>/<ol>/<li>替代纯<div>列表,浏览器对它们有原生优化路径 - 移除“只为撑样式”而存在的包裹层,比如仅用于
margin或padding的空<div> - 避免用
<table>布局长列表——它的 layout 算法是 O(n²) 级别,100 行就能明显拖慢重排 - 检查是否误用了
<section>、<article>等块级语义标签包裹单个列表项,它们会触发额外的节区计算
display: none 不等于卸载,该删还得删
很多开发者以为给隐藏区域加 display: none 就万事大吉,但 DOM 节点还在,样式计算、继承链、无障碍树遍历照常进行。尤其当列表分页或 Tab 切换时,所有“隐藏页”的节点仍在内存里占位。
真正有效的做法是:在用户切换前,把非当前页的整个子树从 DOM 中移除(el.remove()),而不是藏起来。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
立即学习“前端免费学习笔记(深入)”;
- Tab 切换时,先
currentTab.remove(),再container.appendChild(newTab),而非靠 class 控制显隐 - 折叠面板展开前才动态生成内容,闭合后调用
panel.innerHTML = ''清空并释放引用 - 慎用
visibility: hidden—— 它仍参与布局,比display: none更重 - 如果必须保留 DOM(如需快速切回),至少用
aria-hidden="true"+inert属性让浏览器跳过可访问性遍历
虚拟滚动必须自己控制 scrollTop 和 transform
第三方虚拟滚动库(如 react-window)底层也是靠 transform: translateY() 移动容器,并只渲染可视区 ±N 条。但如果你手写,最容易错的是:用 top 或 margin-top 模拟滚动 —— 这会强制重排,性能反而更差。
- 容器设固定高度 +
overflow: auto,内部用<div class="phantom" style="height: calc(100px * 1000)">撑高滚动条 - 真实内容列表用
position: absolute; top: 0; left: 0; width: 100%;,并通过style.transform = `translateY(${offset}px)`移动 - 监听
scroll事件时,读取container.scrollTop计算起始索引,然后批量更新innerHTML或用DocumentFragment插入 - 避免在滚动过程中频繁读取
offsetHeight、getBoundingClientRect()—— 这些会触发同步重排
移动端滚动卡顿,先关掉 passive false
安卓 WebView 和旧版 iOS Safari 对非 passive 的 touchstart/scroll 事件极其敏感。只要监听函数里没加 { passive: false },浏览器就默认不能阻止默认滚动行为;但一旦你写了 event.preventDefault(),就会触发强制同步重排,卡顿立刻出现。
- 滚动监听一律用
addEventListener('scroll', handler, { passive: true }) - 真需要阻止滚动(比如下拉刷新),改用
touchmove+{ passive: false },且仅在满足条件时调用preventDefault() - 避免在
scroll回调里直接操作 DOM 样式,改用requestAnimationFrame批处理 - 检查是否无意中给列表容器加了
will-change: scroll-position—— 这在低端安卓机上反而引发合成层爆炸
真实项目里最常被忽略的点:虚拟滚动的 offset 计算必须用整数像素,且不能依赖浮点 scrollLeft/scrollTop 值做索引映射。小数会导致渲染错位、闪烁,尤其在 DPR ≠ 1 的设备上。要么四舍五入,要么用 Math.floor(scrollTop / itemHeight) 确保索引稳定。


















