Skip to content

ScrollIntoViewIfNeeded 在微前端场景下失效

问题背景

ScrollIntoViewIfNeeded 在微前端下,滚动效果失效。

问题原因

原因是其内部使用 parentNode === document 判断是否在 document 场景下吗,但是实际上这里的 document 是一个 proxyDocument,非全局的 document,因此永远返回 false

解决方案

https://github.com/umijs/qiankun/pull/2415/files

代理 Node.prototype 上的 parentNode 属性。如果当前处于子应用的运行环境,那么使用 parentNode 指向子应用的 proxyDocument,否则指向全局的 document。 判断子应用的运行环境的时候,通过 window 属性 get/set 的来全局设置。(这样虽然不一定准确,但是也没有更好的方法能去判断了)。

后来又产生问题:

在开发环境下,以上改动生效了,该库可以滚动。但是在生产环境下,这种改动却失效了。

原因是,在开发环境下,该库的代码是 xx.parentNode === window.document,但是在生产环境下,该库的代码是 xx.parentNode === document。当代码执行时,由于没有直接调用 window.xxx,没有触发 windowget 方法,导致没有将当前 app 的运行环境保存下来,导致最终的检测失败。

解决方案为,访问 parentNode 时,手动调用一下 window.__NULL__ 触发一下 get 方法(hack way)。

后续问题

经过 qiankun 改造后,发现滚动又失效了。原因是,在 qiankun 沙盒中,会在 bootstrapmount 生命周期里,分别创建 proxyDocument。这就导致 bootstrap 创建的沙盒,执行的 js 访问到的 document 指向的是 bootstrap 创建的 proxyDocument。但是等到 App 挂载完成后,mount 生命周期又创建了一个 proxyDocument。此时 App bootstrap 沙盒里的 js 访问的 window.document 指向的是 mount 生命周期的 proxyDocument。这就导致一个奇怪的现象:document === window.document 不成立。原因就在于 window 是同一个,但是 document 是多个。

解决方案为:如果 window 下已经存在了 proxyDocument,那么就不应该再创建 document,而是复用之前的 proxyDocument