一、基本用法:监听 window 的 scroll 事件
1. 使用 useEffect 添加和清除事件监听
在 React 函数组件中,我们通常使用 useEffect 来添加事件监听,并在组件卸载时移除,防止内存泄漏。
import React, { useEffect, useState } from 'react';
function ScrollExample() {
const [scrollY, setScrollY] = useState(0);
useEffect(() => {
// 定义一个处理滚动的函数
const handleScroll = () => {
setScrollY(window.scrollY); // 获取垂直滚动位置
};
// 添加滚动事件监听
window.addEventListener('scroll', handleScroll);
// 清除事件监听(重要!)
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []); // 空依赖数组,只在组件挂载和卸载时执行
return (
<div style={{ height: '200vh' }}> {/* 让页面可以滚动 */}
<h2>当前滚动位置:{scrollY}px</h2>
</div>
);
}
export default ScrollExample;
二、进阶用法:节流(throttle)优化性能
由于 scroll 事件触发非常频繁,直接在事件处理函数里做复杂操作(如 DOM 查询、大量计算等)可能会影响性能。推荐使用 节流(throttle) 或 防抖(debounce) 进行优化。
使用 lodash 的 throttle(推荐)
首先安装 lodash 和 throttle 方法(或者只引入 throttle):
npm install lodash
# 或者按需引入以减少打包体积
npm install lodash.throttle
然后使用 throttle 优化滚动事件:
import React, { useEffect, useState } from 'react';
import { throttle } from 'lodash'; // 或者 import throttle from 'lodash.throttle';
function ScrollExample() {
const [scrollY, setScrollY] = useState(0);
useEffect(() => {
// 使用 throttle 包装滚动处理函数,每 100ms 最多执行一次
const handleScroll = throttle(() => {
setScrollY(window.scrollY);
}, 100);
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
handleScroll.cancel(); // 取消防抖/节流函数的待执行任务(lodash 特有)
};
}, []);
return (
<div style={{ height: '200vh' }}>
<h2>当前滚动位置(节流优化):{scrollY}px</h2>
</div>
);
}
export default ScrollExample;
注意:
如果不想引入整个 lodash,可以使用lodash.throttle,或者自己实现一个简单的 throttle 函数。
三、自定义简单的 throttle 函数(不依赖 lodash)
如果你不想引入 lodash,可以自己写一个简单的 throttle:
function throttle(func, limit) {
let inThrottle;
return function (...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
然后在组件里这样使用:
useEffect(() => {
const handleScroll = () => {
setScrollY(window.scrollY);
};
const throttledHandleScroll = throttle(handleScroll, 100);
window.addEventListener('scroll', throttledHandleScroll);
return () => {
window.removeEventListener('scroll', throttledHandleScroll);
};
}, []);
四、监听某个元素的滚动,而不是 window
有时候你可能想要监听页面内某个可滚动容器(如 <div style={{ overflow: 'scroll' }}>)的滚动事件,而不是整个窗口。
示例:
import React, { useEffect, useRef, useState } from 'react';
function ElementScrollExample() {
const scrollRef = useRef(null);
const [scrollTop, setScrollTop] = useState(0);
useEffect(() => {
const element = scrollRef.current;
const handleScroll = () => {
setScrollTop(element.scrollTop);
};
element.addEventListener('scroll', handleScroll);
return () => {
element.removeEventListener('scroll', handleScroll);
};
}, []);
return (
<div>
<h3>容器内滚动位置:{scrollTop}px</h3>
<div
ref={scrollRef}
style={{
height: '200px',
overflowY: 'scroll',
border: '1px solid #ccc',
}}
>
<div style={{ height: '1000px', background: '#f0f0f0' }}>
滚动我试试 👇
</div>
</div>
</div>
);
}
export default ElementScrollExample;
五、常见应用场景
- 导航栏透明/显隐:根据页面滚动位置显示/隐藏导航栏。
- 滚动动画:元素进入视口时触发动画(可结合 Intersection Observer 更高效)。
- 回到顶部按钮:滚动一定距离后显示“返回顶部”按钮。
- 无限滚动 / 分页加载:滚动到底部时加载更多内容。
- 视差滚动效果。
六、推荐替代方案:Intersection Observer(用于元素可见性检测)
如果你只是想检测某个元素是否进入或离开视口(比如触发动画、懒加载等),推荐使用 IntersectionObserver,它比监听 scroll 更高效。
总结
| 场景 | 推荐方式 |
|---|---|
| 监听整个页面滚动 | window.addEventListener('scroll', handler) |
| 优化频繁触发 | 使用 throttle 或 debounce |
| 监听某个 div 滚动 | 对该 DOM 元素添加 element.addEventListener('scroll', ...) |
| 检测元素是否进入视口 | 使用 IntersectionObserver |

8104

被折叠的 条评论
为什么被折叠?



