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,没有触发 window 的 get 方法,导致没有将当前 app 的运行环境保存下来,导致最终的检测失败。
解决方案为,访问 parentNode 时,手动调用一下 window.__NULL__ 触发一下 get 方法(hack way)。
后续问题
经过 qiankun 改造后,发现滚动又失效了。原因是,在 qiankun 沙盒中,会在 bootstrap 和 mount 生命周期里,分别创建 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。
