這次,我終于把無限滾動寫對了
無限滾動這個功能,如果實現得當,簡直是絲滑流暢;但一旦出問題,體驗立馬崩塌。
相信你也遇到過這種情況:
剛想點擊某個內容,頁面突然加載新數據,元素跳動,手指點空,一臉問號。
為了不重蹈覆轍,我決定在我的 React 項目中,“認真” 實現一次無限滾動 —— 要性能好、體驗穩、不卡頓、無抖動。
圖片
核心挑戰:讓滾動真正「無限」且「順滑」
從用戶角度看,無限滾動就是:滾到頁面底部,自動加載更多內容。
聽起來簡單,實現起來卻暗藏不少坑:
- 性能問題:如果監聽 scroll 事件不加控制,很容易導致頁面卡頓;
- 重復請求:過度觸發接口,容易把服務器拉爆,甚至被限流;
- UI 抖動:加載數據位置處理不當,頁面就會跳動、錯位,影響體驗。
于是,我換了種思路,從根源解決這些問題。
核心工具:Intersection Observer API
傳統方式一般是監聽 scroll 事件 + 判斷滾動位置。但這種方式有幾個缺陷:
- 滾動監聽觸發頻繁,性能壓力大;
- 需要手動計算位置,誤差多;
- 難以精準判斷「用戶是否到達底部」。
所以我選擇了 Intersection Observer。
這是一種現代瀏覽器提供的異步監聽 API,能精準判斷某個元素是否進入視口 —— 完美適配無限滾動的“觸底加載”場景。
為什么選它?
- ? 高性能:異步觸發,瀏覽器原生優化,幾乎零性能負擔;
- ? 高精度:只在元素真的進入視口時才觸發;
- ? 易管理:不再需要頻繁監聽 scroll 事件或手動判斷位置。
實現步驟(以 React 為例)
1. 初始化組件狀態
用于管理:當前數據列表、加載狀態、頁碼、是否還有更多數據等。
const [items, setItems] = useState([]);
const [page, setPage] = useState(1);
const [isLoading, setIsLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
2. 編寫數據請求函數
根據頁碼分頁請求數據,并更新狀態。
const fetchData = async () => {
if (isLoading || !hasMore) return;
setIsLoading(true);
const res = await fetch(`/api/list?page=${page}`);
const newItems = await res.json();
setItems(prev => [...prev, ...newItems]);
setHasMore(newItems.length > 0);
setPage(prev => prev + 1);
setIsLoading(false);
};
3. 設置 Intersection Observer
使用 useEffect 監聽底部 sentinel 元素是否進入視口。
const observer = useRef();
useEffect(() => {
if (isLoading) return;
const target = document.querySelector("#sentinel");
if (!target) return;
observer.current = new IntersectionObserver(entries => {
if (entries[0].isIntersecting && hasMore) {
fetchData();
}
});
observer.current.observe(target);
return () => observer.current?.disconnect();
}, [isLoading, hasMore]);
4. 渲染列表與 sentinel 元素
return (
<div className="list">
{items.map(item => (
<Item key={item.id} data={item} />
))}
{isLoading && <LoadingSpinner />}
<div id="sentinel" />
</div>
);
實用建議與細節處理
?? 節流 / 防抖:可以給 fetchData 添加 debounce,防止用戶滾得太快導致并發請求。
?? 無更多數據提示:加個 hasMore 判斷,避免接口反復觸發。
?? 組件卸載時清理:別忘了在 useEffect 中 disconnect(),防止內存泄漏。
最終效果
通過 Intersection Observer API,我的無限滾動實現終于達到了「絲滑不抖動、加載不卡頓、邏輯清晰」的理想狀態。
和傳統 scroll 監聽相比:
- 不用手動算距離;
- 性能更優;
- 用戶體驗更流暢。
總結
無限滾動并不難,關鍵在于用對工具,處理好邊界。
? 使用 Intersection Observer 實現精準觸發;
? 結合防抖和 hasMore 判斷避免重復請求;
? 做好清理,保持組件干凈;
這套方案,在中小規模列表加載中非常穩定。
如果你也曾經或正在實現無限滾動,不妨試試這種方式。
有經驗、有坑、有疑問?歡迎留言,一起討論。