2023-08-27 06:08:54
使用 JavaScript 的 Intersection Observer API 实现高性能无限滚动的核心步骤如下:通过监听占位元素(sentinel)的视口交叉状态触发异步加载,结合阈值控制、请求防重与错误处理优化性能。
一、实现原理与优势性能:无需高频计算 scrollTop 或元素位置,减少布局抖动。
功能:天然支持懒袜散加载、无限陪毁滚动等场景,代码更简洁。
HTML 结构定义内容容器与占位元素(sentinel),示例:
<div id="container"> <div class="item">内容项 1</div> <div class="item">内容项 2</div> <!-- 更多项目 --> <div id="sentinel"></div> <!-- 触发加载的占位符 --></div>创建观察器初始化观察器并设置阈值(如 0.1,即元素 10% 可见时触发):
const container = document.getElementById('container');const sentinel = document.getElementById('sentinel');const observer = new IntersectionObserver((entries) => { const entry = entries[0]; if (entry.isIntersecting) { loadMoreContent(); // 触发加载逻辑 }}, { threshold: 0.1 });observer.observe(sentinel); // 开始观察加载数据函数模拟异步请求并插入新内容,加载完成后重新观察:
function loadMoreContent() { // 1. 取消观察避免重复请求 observer.unobserve(sentinel); // 2. 模拟异步请求 fetch('/api/more-items') .then(res => res.json()) .then(items => { items.forEach(text => { const item = document.createElement('div'); item.className = 'item'; item.textContent = text; container.appendChild(item); }); }) .catch(error => { console.error('加载失败:', error); // 错误处理(如重试或提示) }) .finally(() => { // 3. 重新观察(无论成功失败) observer.observe(sentinel); });}避免重复请求
在 loadMoreContent 中调用 unobserve(sentinel),请求完成后再 observe(sentinel)。
维护加载状态标志(如 isLoading),防止并发请求。
错误处理与重试
捕获网络错误,提供用户重试按钮或自动重试机制。
示例:let retryCount = 0;function loadMoreContent() { if (isLoading) return; isLoading = true; observer.unobserve(sentinel); fetch('/api/more-items') .then(/* ... */) .catch(error => { if (retryCount < 3) { retryCount++; setTimeout(loadMoreContent, 1000 * retryCount); // 指数退避重试 } else { showErrorToUser(); // 显示错误提示 } }) .finally(() => { isLoading = false; observer.observe(sentinel); });}
分页状态管理
使用页码(page)或游标(cursor)跟踪已加载数据,避免重复请求同一页。
示例:let currentPage = 1;function loadMoreContent() { fetch(`/api/items?page=${currentPage}`) .then(/* ... */) .then(items => { if (items.length === 0) { observer.unobserve(sentinel); // 无更多数据时停止观察 } else { currentPage++; // 插入内容... } });}
可访问性降级方案
为不支持 Intersection Observer 的浏览器添加“加载更多”按钮:if (!('IntersectionObserver' in window)) { const button = document.createElement('button'); button.textContent = '加载更多'; button.onclick = loadMoreContent; container.appendChild(button);}