实验:揣测react路由下的滚动条处理

425 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

前言

在这篇文章中(实验:揣测浏览器history下的滚动条处理),我们测试了浏览器历史记录前进后退的一些行为,这次我们来测试react路由history模式下滚动条处理,我们知道路由模式无非是基于hash和history两种模式,通过监听其对应的行为而做出页面的更改。

以history模式为例,主要是通过window.history这个api监听浏览器地址的变化,我们也知道,在浏览器当前页新开一个页面,通过history触发的行为称为push,而前进后退则称为pop。主要是因为history维护的是一个地址栈。

在上一篇中我们测试了在浏览器 history触发push的行为滚动条是不会变化的,push地址出现的现象是地址改变了,而实际上页面并没有做出更改的行为。当新push的页面改变原本的dom结构,是否会触发滚动位置的复用呢?

实验

这里我们以react-router-dom的v5版本进行测试。

yarn add react-router-dom@5.0.0

在路由使用之前,我们需要处理高版本react使用v5路由失效的问题,处理方法是把包裹根节点的组件 React.StrictMode删除掉,宽松策略下就可以使用当前路由。

下面是页面的基本配置:

//App页面
function App() {
  return (
    <div className="App">
      <Router>
        <Switch>
        <Route path='/' component={Home} exact></Route>
        <Route path="/a" component={A} exact></Route>
        <Route path="/b" component={B} exact></Route>
        <Route path="/c" component={C} exact></Route>
        </Switch>
      </Router>
    </div>
  );
}

//Home组件
function Home(){
  return <>
   <Link to='/a'>A</Link>
   <Link to='/b'>B</Link>
   <Link to='/c'>C</Link>
  </>
}

home页面.png

当我们点击A跳转到/a页面,下面是A页面的代码,而此时A页面的高度是存在滚动条的。(后续页面的结构都是和A组件一样,只是更改了背景或者高度)

//A组件
function A() {
    return (
        <div style={{
            background: 'green',
        }}>
            <div style={{
                width: '500px',
                height: '500px',
                backgroundColor: 'green'
            }}>
            </div>
            <Link to='/b'>B</Link>

            <div style={{
                width: '500px',
                height: '500px',
                backgroundColor: '#008c8c'
            }}>
            </div>

            <div style={{
                width: '500px',
                height: '500px',
                backgroundColor: 'green'
            }}>
            </div>
        </div>
    )
}

当我们跳转到A页面时,此时滚动条是置顶的行为。

A页面.png

当我们下滑A页面到一定的滚动位置时,我们点击跳转到B页面,此时B页面并不会置顶,而是复用了A页面的滚动位置。

B页面.png

我们点击跳转到C页面,此时页面仍然复用了B页面的位置。我们将C页面滚动到底部。

C页面.png

此时我们回退页面、前进页面,发现滚动条的位置是被记录的。而网络面板中并不存在页面的请求。

结合上一篇记录的现象,我们初步得出这样的规律:

  • 因为路由模式下都是在同一个html页面,所以历史记录的前进回退并不会导致页面的重新请求。
  • 往地址栏push一个地址,浏览器是会复用上一个页面的滚动位置的,除非新开的页面没有滚动条。

此时我们可以验证一种猜想,当新开的页面高度比上一个页面小呢,经过测试发现,滚动条是移动到底部。

如果新开的页面一开始是没有滚动条的,经过数据请求之后出现了滚动条,滚动条是否会复用呢?

验证这个猜想,我们通过计时器模拟:

function C() {
    const [list, setList] = useState([])
    useEffect(() => {
        setTimeout(() => {
            setList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
        }, 1000)
    }, [])
    return (
        <div style={{
            background: 'green',
        }}>
            <div style={{
                width: '500px',
                height: '100px',
                backgroundColor: 'yellow'
            }}>
            </div>
            <Link to='/a'>a</Link>


            <div style={{
                width: '500px',
                height: '100px',
                backgroundColor: '#008c8c'
            }}>
            </div>

            <div style={{
                width: '500px',
                height: '100px',
                backgroundColor: 'yellow'
            }}>
            </div>

            {list.map((item, index) => <div key={index} style={{ width: '100px', height: '100px', background: 'green' }}>
                {item}
            </div>)}
        </div>
    )
}

测试结果发现初始界面没有滚动条,经过数据请求之后页面高度足以存在滚动条了,可是滚动条是置顶的。

我们通过调整页面的初始高度,使得初始页面是存在滚动条的,我们再把数据请求改成持续性的setInterval计时器,每次增加一点点页面高度。我们发现页面会记录上次滚动条的位置,一开始页面会在底部,随着页面高度的增长,滚动条也随着滚动,直到滚动到上一个页面的滚动条位置时才停止滚动。

结论

以下的结论基于v5路由。

  • 新push的页面滚动条位置会继承上一次页面滚动条位置,如果位置不够,到就最底部
  • 浏览器前进后退都能记录之前的滚动条记录
  • history的跳转,继第一条现象,当后续往页面插入内容时,滚动条仍然会往下滚动,直到停到上一个页面离开时的滚动位置。(条件,跳转的页面存在滚动条)
  • history的跳转,继第一条现象,当后续往页面插入内容时,滚动条不会往下滚动,会置顶。(条件,跳转的页面不存在滚动条)