监听 window 的 scroll 事件

一、基本用法:监听 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;

五、常见应用场景

  1. 导航栏透明/显隐​:根据页面滚动位置显示/隐藏导航栏。
  2. 滚动动画​:元素进入视口时触发动画(可结合 Intersection Observer 更高效)。
  3. 回到顶部按钮​:滚动一定距离后显示“返回顶部”按钮。
  4. 无限滚动 / 分页加载​:滚动到底部时加载更多内容。
  5. 视差滚动效果

六、推荐替代方案:Intersection Observer(用于元素可见性检测)

如果你只是想检测某个元素是否进入或离开视口(比如触发动画、懒加载等),推荐使用 IntersectionObserver,它比监听 scroll 更高效。


总结

场景推荐方式
监听整个页面滚动window.addEventListener('scroll', handler)
优化频繁触发使用 throttle 或 debounce
监听某个 div 滚动对该 DOM 元素添加 element.addEventListener('scroll', ...)
检测元素是否进入视口使用 IntersectionObserver
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值