别再折磨你的 React 应用:顺滑如黄油的 Scroll-To-Top 组件

发布: (2026年3月3日 GMT+8 00:58)
4 分钟阅读
原文: Dev.to

Source: Dev.to

陷阱:追踪每一个像素

当我第一次实现这个功能时,掉进了经典的陷阱:在 React 状态中存储精确的 window.scrollY 位置。用户滚动的每一个像素都会触发状态更新,导致组件每秒重新渲染数百次。这是一个巨大的资源消耗,却没有任何必要。

解决方案:阈值状态 & CSS 魔法

我们不需要追踪精确的滚动深度,只关心一件事:用户是否已经滚动超过阈值?(本例中为 300 px)。我们只跟踪一个布尔值,这意味着 React 只会重新渲染两次——一次是按钮出现时,另一次是按钮消失时。

import { useState, useEffect } from 'react';
import './ScrollToTop.css';

const ScrollToTop = () => {
    const [isVisible, setIsVisible] = useState(false);

    useEffect(() => {
        const handleScroll = () => {
            // 仅在跨过 300px 标记时切换状态
            setIsVisible(window.scrollY > 300);
        };

        // { passive: true } 选项对滚动性能至关重要!
        window.addEventListener('scroll', handleScroll, { passive: true });

        return () => {
            window.removeEventListener('scroll', handleScroll);
        };
    }, []); // 空依赖数组确保监听器只挂载一次

    const handleClick = () => {
        window.scrollTo({
            top: 0,
            behavior: 'smooth'
        });
    };

    return (
        
            
                Scroll to Top ⇑
            
        
    );
};

export default ScrollToTop;

样式:抛弃 display: none

你无法对在 display: blockdisplay: none 之间切换的元素进行动画,它会瞬间出现或消失。为了实现顺滑的淡入淡出,我们使用 opacityvisibility,并配合 pointer-events: none,让不可见的按钮不会阻挡底层点击。

.btn {
    padding: 12px 20px;
    border: none;
    border-radius: 8px;
    position: fixed;
    bottom: 20px;
    right: 20px;
    z-index: 1000;
    background-color: slateblue;
    color: white;
    cursor: pointer;
    /* 通过过渡 opacity 和 visibility 实现平滑淡入淡出 */
    transition: opacity 0.4s ease, visibility 0.4s ease, transform 0.3s ease;
    box-shadow: 0 4px 6px rgba(0,0,0,0.2);
}

.btn:hover {
    background-color: orange;
    transform: translateY(-3px);
}

.show {
    opacity: 0.85;
    visibility: visible;
}

.hide {
    opacity: 0;
    visibility: hidden;
    pointer-events: none; /* 防止不可见时的点击 */
}

为什么这个组件是赢家

  • 零拖拽 – 摒弃像素级追踪后,浏览器可以轻松运行。
  • 被动监听器{ passive: true } 告诉浏览器监听器不会阻止默认的滚动行为,从而保持滚动的极致流畅。
  • 可访问且简洁aria-label 让屏幕阅读器友好,CSS 动画则提供了高级 UI 体验。

随意把它拎进你的项目!在评论里告诉我你在 React 项目中是如何处理滚动事件的——有没有比标准 useEffect 更喜欢的自定义 Hook?

0 浏览
Back to Blog

相关文章

阅读更多 »