You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在用户离开页面时设置session key实现滚动位置记忆功能

原有代码存在的问题
  • 事件绑定逻辑错误:监听滚动的useEffect把scrollPosition作为依赖,每次滚动更新state后都会重新绑定滚动事件,会造成性能损耗;且直接给window.onscroll赋值会覆盖页面内其他绑定的滚动监听。
  • 闭包陷阱+重复绑定unload:unload事件直接写在组件顶层,每次组件重渲染都会新增一个unload监听,且每个监听拿到的scrollPosition都是当前渲染闭包内的旧值,最终存储的滚动位置大概率是错误的初始值。
  • 缺少事件清理逻辑:组件卸载时没有移除滚动、unload事件的监听,会造成内存泄漏。
  • 需求未完全实现:没有做“当前路径和上次保存路径不一致时移除旧存储”的逻辑。
  • 类型和变量混乱:scrollPosition初始值设为空字符串,但实际存储的是数字类型;pathName重复声明多次。
  • 性能损耗:每次滚动都触发setState导致组件重渲染,完全没必要。
优化后的实现方案
import React from 'react';

const ScrollRestore = () => {
  // 用ref存滚动位置,避免频繁更新state,同时解决闭包问题
  const scrollRef = React.useRef(0);
  const currentPath = document.location.pathname.substr(1);

  React.useEffect(() => {
    // 页面加载时先处理历史存储
    try {
      const lastSaved = JSON.parse(sessionStorage.getItem('scrollPosition'));
      if (lastSaved) {
        if (lastSaved.pathName === currentPath) {
          // 路径匹配,恢复滚动位置
          window.scrollTo(0, lastSaved.position);
        } else {
          // 路径不匹配,删除旧存储
          sessionStorage.removeItem('scrollPosition');
        }
      }
    } catch (e) {
      // 捕获storage读取/解析异常,避免页面崩溃
      sessionStorage.removeItem('scrollPosition');
    }

    // 滚动监听:只更新ref,不触发重渲染
    const handleScroll = () => {
      scrollRef.current = window.scrollY;
    };
    // 页面卸载前保存滚动位置
    const handleBeforeUnload = () => {
      if (scrollRef.current > 0) {
        try {
          sessionStorage.setItem('scrollPosition', JSON.stringify({
            position: scrollRef.current,
            pathName: currentPath
          }));
        } catch (e) {
          // 忽略storage写入异常
        }
      }
    };

    window.addEventListener('scroll', handleScroll, { passive: true });
    window.addEventListener('beforeunload', handleBeforeUnload);

    // 清理函数:组件卸载时移除所有监听
    return () => {
      window.removeEventListener('scroll', handleScroll);
      window.removeEventListener('beforeunload', handleBeforeUnload);
    };
  }, [currentPath]);

  // 你的组件其他逻辑
  return <div>{/* 页面内容 */}</div>;
};

优化点说明

  • 用useRef存储滚动位置,避免滚动触发组件重渲染,同时解决了unload事件的闭包陷阱,总能拿到最新的滚动值
  • 所有事件绑定都放在同一个useEffect内,返回清理函数解绑事件,不会出现重复绑定和内存泄漏问题
  • 补充了路径不匹配时删除旧存储的逻辑,完全符合需求
  • 用addEventListener绑定事件,不会覆盖其他同类型监听,scroll事件加passive参数提升滚动性能
  • 增加了storage操作的异常捕获,避免浏览器禁用存储、存储数据异常时导致页面崩溃
  • 所有变量只声明一次,类型统一,逻辑更清晰

内容的提问来源于stack exchange,提问作者user16468845

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 14:48:02