怎么给新手解释 react 的 spa 应用锚点跳转的问题

更多内容在我的语雀:www.yuque.com/xiezhongfu/…css

前言

咱们先区分下“页面”这个词:html

  • 单页面应用(spa)就只有一个页面
  • 文中所指的页面是从浏览器的 url 角度去理解的,只要 protocol + port + pathname 不一样,浏览器就认为是新页面

问题是什么

<Route path="/foo" componet={Foo} />
<Route path="/bar" componet={Bar} /> // Foo 页面组件 和 Bar 页面组件都是静态化组件(这两个组件里面的数据都是不变的) .... <Link to="/foo">foo</Link> <Link to="/bar#test">bar</Link> 复制代码

单页面应用,在同一个页面使用 hash 跳转到页面不一样位置(锚点跳转)。此功能是不正常的。
还能够看看前人发的 issue 参见:github.com/ReactTraini…。针对这个问题也有相应解决方案:react

顺便说一下,scrollIntoView 和 scrollTo 能够设置平滑滚动;在滚动区域 css 设置 scroll-behavior 也能够平滑滚动,粗暴一点咱们给 * 设置 scroll-behavior 吧。git

猜想为何

让咱们在看下这段代码github

<Route path="/foo" componet={Foo} />
<Route path="/bar" componet={Bar} /> // Foo 页面组件 和 Bar 页面组件都是静态化组件(这两个组件里面的数据都是不变的) .... <Link to="/foo">foo</Link> <Link to="/bar#test">bar</Link> 复制代码

在不一样的页面跳转不一样锚点,history 有更新,初始化渲染组件,功能正常。
在同一个页面跳转不一样锚点,history 有更新,从新渲染原组件,功能不正常。
原生方式下,就算在同一个页面的不一样锚点间跳转,功能正常。web

场景 A
假设咱们这样路由了 2 个组件,咱们先点击了 foo,渲染了 Foo 组件。而后咱们点击 bar ,pathname 从 /foo 路由到 /barBar 组件会初始化渲染,页面效果是直接定位到了 Bar 组件渲染的页面中的 test 锚点。
场景 B
假设在 Bar 页面内有一个本身页面内的锚点,咱们点击它,虽然这个锚点就在同一个页面,锚点跳转失效。效果是 url 上看到锚点变了,可是没跳到对应的新锚点。
由于使用了 Link 组件,在 Bar 页面内点击了一个新的锚点,Link 组件内使用了 push 或者 replace 产生了新的 window.history 记录,这个新 window.history 的 pathname 和之前同样,只是有新的 hash。而后就去 Route 里去找匹配的组件,发现匹配到了 Bar 组件 。由于已经渲染过了,那就从新渲染吧。
由于 protocol,port,pathname 都没变,只是 hash 变了,浏览器认为这是老页面上的一个新锚点,那就在老页面上跳转吧。可是页面组件若是在从新渲染,也许在浏览器刚想要跳新锚点的时候重现渲染致使锚点没了,跳转失败。chrome

咱们总结下这个过程:
在 react 应用中的锚点跳转的实现方案是:Link 组件。跳转新页面锚点成功,可是跳转同页面锚点失败。
Link 组件默认行为包括调用 history 库的 push 或 replace,组件会初始化渲染或者从新渲染。渲染的过程是 js 对象转为 html 的过程。若是这个过程还没结束,html 还没及时出现,锚点功能就失效了。
。咱们知道锚点能成功,必定是浏览器在跳转的时候有 html 节点上有对应的 id 或 name(好比:a 标签可使用 name 用来标记别人能够跳到它这),若是没有那就跳转失败。浏览器

以上推理都是根据经验猜想,咱们怀疑:
场景 A 在由于是新页面,在浏览器跳转锚点前 react 组件初始化渲染已经完成, html 已经有对应锚点了。
场景 B 因为是同一个页面,在浏览器跳转锚点前 react 组件从新渲染还没完成,跳转失败了。
那浏览器究竟是怎么执行锚点跳转的呢?开始查材料......react-router

这真的是缘由么

咱们先看标准文档是怎么解释锚点的:html.spec.whatwg.org/multipage/b…oop

在看 chrome 是怎么处理的:cs.chromium.org/chromium/sr… (这是巢鹏大佬的解答)

image.png

咱们再来看回顾下遇到的问题:

  • Link 组件新跳转带锚点的页面,组件初始化渲染,锚点功能正常
  • Link 组件在同一个页面跳转新锚点,组件从新渲染,锚点功能异常
  • 若是是 a 标签无论是否是新页面,锚点功能都正常

从 chrome 的实现和咱们遇到的问题中能体会出关键在因而不是新页面。整个过程说得比较绕,指望是表达清楚了。

最后

在这个过程当中还遇到一些老知识,复习下吧:

相关文章
相关标签/搜索