ReactJS 页面跳转保存当前scrollTop回来时,自动移动到上次浏览器的位置

本文探讨了在React项目中如何实现在页面跳转后返回时,自动滚动到之前的位置。作者提到使用react-router的onEnter和onLeave事件来管理scrollTop,但直接在每个路由组件中添加这些事件会变得繁琐。为了解决这个问题,作者选择在项目中的App组件的componentWillMount方法中统一处理这些事件,以此简化router.jsx文件并实现全站适用的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在移动端的操作的时候,相信大家都遇到到这种情况,翻了好几页了,点击一项进去查,然后回来的时候,还想回来我原来的位置。

google上也找了一此,有一个组件,但是好像是如果想实现这个功能,页面就得用那个组件包进来。

一个项目已经写了几十个页面了,每个页面都去把他包进去,然后再实现组件里的方法,太痛苦了。

后来发现router里有onEnter onLeave事件,那就在这里做文章吧。这就方便多了,就在router.jsx每个里面加 onEnter={()=>{}} onLeave = {()=>{..}} 如果以后再添加页面,又得复制一下这两个事件,这样也很麻烦是吧。

以前用 asp.net 要给页面上所有button都添加统一的事件,或CS程序里哪页面都添加事件等功能,就在想我也统一的,在给router里的信息统一添加 onEnter onLeave事件呢?这样我的router.jsx文件里是不是简洁多了。

这样一来,我只要找到Router里第一个组件,在那个做文章不就可以了。其它的什么都不用大动,由于我们在写项目的router里的第一组件名是 app如下图所示:
这里写图片描述

那就找到 App.这个组件,在componentWillMount这个方法里。开始对路由进行统一添加onEnter onLeave

  componentWillMount(){
    this.__PreserveRouterComponentEnterAndLeveScrollTop();
  }
  /**
   * 保存路由切换时的 scroll top 的值。
   * */
  __PreserveRouterComponentEnterAndLeveScrollTop() {
    const { children } = this.props; 
    const { props } = children || {};
    const { routes } = props || {};
    if (!Array.isArray(routes)) {
      return;
    }
    const { childRoutes } = routes[0];
    if (!Utility.isArray(childRoutes)) {
      return;
    }
    const __KeyScroll = 'XTN_ROUTER_SCROLLTOP';
    // 页面离开的时候,记录当前的scrollTop位置
    const __onLeave = (args) => {
      const __Data = this.state[__KeyScroll] || {};
      __Data[args.toLocaleLowerCase()] = window.document.body.scrollTop;
      this.state[__KeyScroll]= __Data;
    };
    // 页面进入的时候,查找之前的scrollTop位置,有就更新到之前的位置。
    const __onEnter = (args) => {
      const { location } = args;
      const { pathname } = location;
      const __Data = this.state[__KeyScroll];
      if (__Data && __Data[pathname] && __Data[pathname] > 0) {
        // 为什么这里要晚点时间再更新,因为在切换这后,页面做一些其它的操作所以就设置了这个时间。
        setTimeout(() => {
          window.document.body.scrollTop = __Data[pathname];
        }, 1000);
      }
    };
    // 循环将所有路由,将他们都绑定onLeaveonEnter事件。
    childRoutes.forEach((r) => {
      r.onLeave = __onLeave.bind(r, r.path);
      r.onEnter = __onEnter.bind(r);
    });
    /*
    * 这里就怎么说呢,如果调试了的话,就会发现这个数组有两个,有一个IndexRoute
    * IndexRoute 这个是不会存在于 childRoutes 里面的,所以在这里得单独处理一下。
    * 其实下面的代码还是可以完善的。
    * */
    routes.forEach((r) => {
      const { path, isIndex } = r;
      if (path) {
        r.onLeave = __onLeave.bind(r, r.path);
        r.onEnter = __onEnter.bind(r);
      }
      // 这里就说明是 IndexRoute 这个路由,
      if (isIndex === 1) {
        r.onLeave = __onLeave.bind(r, '/');
        r.onEnter = __onEnter.bind(r);
      }
    });
  }

以上就是我在项目中用来解决,页面回返时再滚动到之前的浏览的位置

<think>我们正在处理一个关于React页面跳转后返页面自动向下滚动的问题。根据用户描述,在React应用中,当从一个页面跳转到另一个页面后返时,页面自动向下滚动,而不是停留在跳转位置。这通常是因为浏览器默认会记录页面滚动位置,但在单页应用(SPA)中,由于页面没有重新加载,可能会导致滚动位置恢复行为不符合预期。 参考引用中提到的[^3]关于滚动监听和锚点跳转的内容,我们可以联想到在React中可以通过控制滚动行为来解决这个问题。 解决方案思路: 1. 使用React Router提供的滚动恢复功能(如果使用React Router v5或更早版本,可能需要自行实现)。 2. 在React Router v6中,可以使用`<ScrollRestoration>`组件。 3. 自定义滚动行为:在离开页面保存滚动位置,返时恢复。 但是,用户提到的是返页面自动向下滚动的问题,这可能是由于浏览器默认行为或者页面布局变化导致的。因此,我们需要确保在返时能够正确恢复到之的滚动位置。 具体步骤: 方法一:使用React Router的ScrollRestoration(v6) -React Router v6中,可以使用`<ScrollRestoration>`组件来管理滚动位置。它会在导航时保存滚动位置,并在返时恢复。 方法二:手动保存和恢复滚动位置(适用于更复杂的情况或旧版本) - 在组件挂载时,读取之保存的滚动位置并设置。 - 在组件卸载保存当前的滚动位置。 然而,用户的问题可能是在返页面自动向下滚动,这可能是由于页面内容尚未完全加载(比如异步数据)就恢复了滚动位置,导致恢复的位置不对。因此,我们需要在数据加载完成后再恢复滚动位置。 另外,注意引用[^2]中提到的IOS微信下滚动失效的问题,虽然问题不同,但也提醒我们要注意不同环境下的滚动行为差异。 实现示例(手动方法): 1. 创建一个上下文(Context)或使用全局状态(如Redux)来存储每个路由的滚动位置。 2. 使用React Router的`useLocation`来获取当前路径,作为存储滚动位置的键。 3. 在页面组件中,使用`useEffect`来设置和恢复滚动位置。 代码示例(使用React Hooks): ```jsx import { useEffect } from 'react'; import { useLocation } from 'react-router-dom'; // 用于存储滚动位置的对象 let scrollPositions = {}; function ScrollManager() { const location = useLocation(); useEffect(() => { // 当离开当前页面时,保存滚动位置 const saveScrollPosition = () => { scrollPositions[location.key] = window.scrollY; }; // 如果之保存当前位置,则恢复 const savedPosition = scrollPositions[location.key]; if (savedPosition !== undefined) { window.scrollTo(0, savedPosition); } else { // 如果是新进入的页面,滚动到顶部 window.scrollTo(0, 0); } // 组件卸载时保存滚动位置 return saveScrollPosition; }, [location.key]); return null; } // 然后在应用的根组件中引入<ScrollManager /> ``` 但是,上述代码可能无法解决异步加载数据后页面高度变化的问题。因此,我们需要在数据加载完成后再恢复滚动位置。 改进:在数据加载完成后恢复滚动位置 我们可以将恢复滚动位置的逻辑放在数据加载完成后的调中。例如,使用一个状态来标记数据是否加载完成,然后在数据加载完成的useEffect中恢复滚动位置。 然而,更通用的做法是使用React Router的`<ScrollRestoration>`组件(v6)并配合自定义行为。 在React Router v6中,可以这样使用: ```jsx import { ScrollRestoration } from 'react-router-dom'; function App() { return ( <div> {/* ...其他路由 */} <ScrollRestoration /> </div> ); } ``` 默认情况下,`<ScrollRestoration>`会尝试在导航时恢复滚动位置。但是,如果页面内容有异步加载,它可能不会等待。因此,我们可以通过`useScrollRestoration`钩子来自定义。 参考React Router文档:https://reactrouter.com/en/main/components/scroll-restoration 如果问题仍然存在,考虑以下可能: - 页面布局在返时发生了变化(比如某些元素的高度变化),导致之保存的滚动位置不再适合。 - 在返时,页面可能重新渲染,而滚动恢复发生在渲染完成之。 另外,用户提到在跳转后返时,从屏幕左边向右滑或从上面向下滑会导致返,这是iOS的默认手势(在React Navigation中也可能出现)。如果不需要,可以禁止该手势,但这是另一个问题。 针对用户的问题,我们主要解决返时滚动位置不正确的问题。 因此,建议首先尝试使用React Router v6的`<ScrollRestoration>`组件。如果项目使用的是旧版本,考虑升级或手动实现。 如果手动实现,注意以下几点: 1. 在页面卸载保存滚动位置。 2. 在页面再次进入时,等待页面布局稳定(比如在useLayoutEffect中恢复,或者等待数据加载完成后恢复)再设置滚动位置。 由于用户的问题描述中并没有说明React Router的版本,我们提供两种方案。 相关问题: 1. React Router v6中如何自定义滚动恢复行为? 2. 如何禁止iOS的滑动返手势? 3. 在React中,如何确保异步数据加载后正确恢复滚动位置?</think>### React 页面跳转后返页面位置异常问题分析 在 React 应用中,页面跳转后返时出现页面自动向下滚动的问题,通常是由以下原因造成的: 1. **浏览器默认行为** 浏览器会记录用户滚动位置,当通过浏览器的后退按钮返时,会自动恢复之的滚动位置。但在 SPA 中,这种机制可能导致位置异常[^3]。 2. **组件状态未重置** 页面组件返时未正确重置内部状态(如 `scrollTop` 值),导致渲染时继承了历史位置。 3. **布局变化影响** 页面跳转期间发生的布局变化(如元素高度改变)会导致滚动位置计算错误。 --- ### 解决方案 #### 方法 1:使用 React Router 的滚动恢复功能(推荐) ```jsx // 安装滚动恢复包 npm install react-router-scroll-top // 在路由配置中使用 import { ScrollToTop } from 'react-router-scroll-top'; function App() { return ( <Router> <ScrollToTop /> {/* 添加此行 */} <Routes> <Route path="/" element={<Home />} /> <Route path="/detail" element={<Detail />} /> </Routes> </Router> ); } ``` #### 方法 2:手动重置滚动位置 ```jsx // 在页面组件中使用 useEffect import { useEffect } from 'react'; import { useLocation } from 'react-router-dom'; const ProductPage = () => { const location = useLocation(); useEffect(() => { // 每次路由变化时滚动到顶部 window.scrollTo(0, 0); }, [location.pathname]); // 监听路径变化 return (...); }; ``` #### 方法 3:禁用浏览器滚动记忆 在路由配置中添加 `scrollRestoration` 控制: ```jsx // 在入口文件 index.js if ('scrollRestoration' in window.history) { window.history.scrollRestoration = 'manual'; } ``` #### 方法 4:处理特定元素滚动 对于内部滚动容器(如 `div`),需单独管理状态: ```jsx const scrollRef = useRef(); // 跳转保存位置 const saveScroll = () => { sessionStorage.setItem('scrollPos', scrollRef.current.scrollTop); }; // 返时恢复位置 useEffect(() => { const savedPos = sessionStorage.getItem('scrollPos'); if (savedPos) { scrollRef.current.scrollTo(0, parseInt(savedPos)); sessionStorage.removeItem('scrollPos'); } }, []); ``` --- ### 关键注意事项 1. **异步内容加载** 若页面包含异步数据,需在数据加载完成后执行滚动恢复: ```jsx useEffect(() => { fetchData().then(() => { window.scrollTo(0, savedPosition); }); }, []); ``` 2. **移动端特殊处理** iOS 设备需额外处理弹性滚动: ```css /* 禁用过度滚动 */ body { overscroll-behavior: contain; } ``` 3. **路由库兼容性** 不同路由库解决方案: - **React Router v5+**:使用 `<ScrollRestoration>` 组件 - **Next.js**:在 `_app.js` 中实现自定义滚动管理 > 建议优先使用 React Router 官方解决方案,其他方法作为备选。测试时需重点关注 iOS 设备表现,因其滚动行为较为特殊[^2]。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值