问题是什么
单页面应用,在同一个页面使用 hash 跳转到页面不同位置(锚点跳转)。此功能是不正常的。
顺便说一下,scrollIntoView 和 scrollTo 可以设置平滑滚动;在滚动区域 css 设置 scroll-behavior 也可以平滑滚动,粗暴一点我们给 * 设置 scroll-behavior 吧。
猜测为什么
让我们在看下这段代码
在不同的页面跳转不同锚点,history 有更新,初始化渲染组件,功能正常。
在同一个页面跳转不同锚点,history 有更新,重新渲染原组件,功能不正常。
原生方式下,就算在同一个页面的不同锚点间跳转,功能正常。
场景 A
假设我们这样路由了 2 个组件,我们先点击了 foo,渲染了 Foo 组件。然后我们点击 bar ,pathname 从 /foo 路由到 /bar 。Bar 组件会初始化渲染,页面效果是直接定位到了 Bar 组件渲染的页面中的 test 锚点。
场景 B
假设在 Bar 页面内有一个自己页面内的锚点,我们点击它,虽然这个锚点就在同一个页面,锚点跳转失效。效果是 url 上看到锚点变了,但是没跳到对应的新锚点。
因为使用了 Link 组件,在 Bar 页面内点击了一个新的锚点,Link 组件内使用了 push 或者 replace 产生了新的 window.history 记录,这个新 window.history 的 pathname 和以前一样,只是有新的 hash。然后就去 Route 里去找匹配的组件,发现匹配到了 Bar 组件 。因为已经渲染过了,那就重新渲染吧。
因为 protocol,port,pathname 都没变,只是 hash 变了,浏览器认为这是老页面上的一个新锚点,那就在老页面上跳转吧。但是页面组件如果在重新渲染,也许在浏览器刚想要跳新锚点的时候重现渲染导致锚点没了,跳转失败。
我们总结下这个过程:
在 react 应用中的锚点跳转的实现方案是:Link 组件。跳转新页面锚点成功,但是跳转同页面锚点失败。
Link 组件默认行为包括调用 history 库的 push 或 replace,组件会初始化渲染或者重新渲染。渲染的过程是 js 对象转为 html 的过程。如果这个过程还没结束,html 还没及时出现,锚点功能就失效了。
。我们知道锚点能成功,一定是浏览器在跳转的时候有 html 节点上有对应的 id 或 name(比如:a 标签可以使用 name 用来标记别人可以跳到它这),如果没有那就跳转失败。
以上推理都是根据经验猜测,我们怀疑:
场景 A 在因为是新页面,在浏览器跳转前 react 组件初始化渲染已经完成, html 已经有对应锚点了。
场景 B 由于是同一个页面,在浏览器跳转前 react 组件重新渲染还没完成,跳转失败了。
那浏览器到底是怎么执行锚点跳转的呢?开始查材料......