IntersectionObserver可高效实现懒加载,它不依赖scroll事件和位置计算,通过监听元素可见性变化天然防抖、不丢帧;配置rootMargin提前触发、threshold控制阈值,并配合sentinel占位节点与状态锁,避免卡顿、漏触发及并发请求。

用 IntersectionObserver 监听滚动触发动态加载
直接监听 window.onscroll 做懒加载,99% 的情况会卡顿或漏触发——因为图片异步加载、广告插入、动态高度变化都会让 scrollTop + innerHeight >= scrollHeight 判断失效。现代方案应绕过 scroll 事件,改用浏览器原生的 IntersectionObserver。
它不依赖滚动位置计算,只关注“元素是否可见”,天然防抖、不丢帧、DOM 高度变化不影响判断。
- 在列表末尾插入一个占位
<div id="sentinel"></div> - 创建 observer 实例时配置
{ rootMargin: "0px 0px 200px 0px", threshold: 0 },提前 200px 触发,避免用户快速滚动时白屏 - 回调中检查
entries[0].isIntersecting为true再调用loadMore() - 加载完成后,重新绑定新占位节点(或复用原节点),并确保有
isLoading状态锁防止并发请求
fetch + insertAdjacentHTML 追加 HTML 片段
后端返回的不是纯数据而是完整 HTML 片段(如 <article><h2>标题</h2>
<p>摘要</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill11445" title="使用华数设计生成高保真HTML原型、演示文稿、动画和设计评审"><img
src="https://img.php.cn/upload/skill/001/246/273/6adaf20e695c2374.jpg" alt="使用华数设计生成高保真HTML原型、演示文稿、动画和设计评审" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill11445" title="使用华数设计生成高保真HTML原型、演示文稿、动画和设计评审">使用华数设计生成高保真HTML原型、演示文稿、动画和设计评审</a>
<p>使用经过文档化的设计技能,从真实的品牌或项目背景中生成HTML原生原型、幻灯片、动态演示、可视化图表和评审流程。</p>
</div>
<a href="/xiazai/skill11445" title="使用华数设计生成高保真HTML原型、演示文稿、动画和设计评审" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div></article>)时,不要先解析再拼字符串,直接插入最高效。
- 用
fetch("/api/articles?page=2")请求,响应类型设为text(不是json),避免 JSON → HTML 转换开销 - 拿到响应体后,调用
document.getElementById("article-list").insertAdjacentHTML("beforeend", htmlText) - 避免用
innerHTML += htmlText,会强制重绘整个容器,长列表下性能断崖式下降 - 插入前可加一层简单校验:比如检查返回内容是否包含
<article> 开头,防止服务端错误返回空或报错 HTML</article>
为什么不能只靠 loading="lazy" 加载整块内容
loading="lazy" 是浏览器级优化,但它只对 <img alt="HTML怎么在滚动页面时懒加载动态内容" > 和 <iframe></iframe> 生效,且仅控制资源请求时机,不适用于「整块文章区块」的按需渲染。
立即学习“前端免费学习笔记(深入)”;
- 它无法触发 JavaScript 逻辑(比如更新页码、禁用按钮、上报埋点)
- 不支持自定义加载阈值或预加载行为(如提前 300px 加载)
- 对
<section></section>、<article></article>或组件级内容完全无效 - 若后端未返回真实 HTML,而是需要前端模板编译,那
loading="lazy"根本无从介入
容易被忽略的边界问题
真正上线后出问题的,往往不是主流程,而是这些细节:
- 占位节点被重复 observe:每次加载完必须调用
observer.unobserve(sentinel),再observer.observe(newSentinel),否则可能多次触发 - 页面初始高度不足一屏,导致 sentinel 一开始就处于相交状态,需在初始化时加
if (sentinel.getBoundingClientRect().top 防止首屏漏加载 - 移动端 WebView(尤其旧版 Android)可能不支持
rootMargin,建议 fallback 到{ threshold: 0.1 }并搭配getBoundingClientRect()简单兜底 - 服务端分页接口没做幂等,用户手抖连点两次“加载更多”,可能重复插入相同文章——应在前端加防抖,或后端用游标(cursor)替代页码


















