likes
comments
collection
share

在 qiankun 中 document.head.appendChild 方法影响到 webpack 代码分割的问题

作者站长头像
站长
· 阅读数 10

此问题并非 qiankun 的 bug

背景

在我们公司的前端项目中,在主应用的根组件 App 中有这样一段代码

简化版代码

import React, { useEffect, useRef } from 'react'

const rawAppendChild = HTMLHeadElement.prototype.appendChild

function App() {
  useEffect(() => {
    const unlisten = history.listen((loc, action) => {
      // xxx 是伪代码
      if(xxx) {
        // 关键代码
        HTMLHeadElement.prototype.appendChild = rawAppendChild
      }
    })
    return () => {
      unlisten()
    }
  }, [])
  
  return <div />
}

这里只有在满足一定条件后才会执行 关键代码

当关键代码执行完毕后,再发子应用的代码分割就会报错

在 qiankun 中 document.head.appendChild 方法影响到 webpack 代码分割的问题

当时我一脸问号,?????,这是什么鬼,webpack 的代码分割怎么可能报错?? 带着难以置信的心情开始了寻找bug的路程

最小化场景复现 gif图

在 qiankun 中 document.head.appendChild 方法影响到 webpack 代码分割的问题

一旦执行完关键代码后,再触发子应用的代码分割,webpack 内部就会报错

底层原因

代码分割的原理

首先在编译期间,webpack 将需要代码分割的代码单独提取到一个js文件中

然后,在打包后的项目初始化阶段,webpack 会先向 window 定义一个函数,我们称之为 JSONP函数

查看打包后的代码,如下图 在 qiankun 中 document.head.appendChild 方法影响到 webpack 代码分割的问题

当点击 button 触发代码分割之后,webpack 会向 document.head 添加一个 script 标签,用来加载 js 文件,文件内部会调用window上的 JSONP函数注意:是通过 document.head.appendChild 方法添加的dom。

查看打包后的代码,如下图 在 qiankun 中 document.head.appendChild 方法影响到 webpack 代码分割的问题

之后浏览器执行新增的js代码,调用window上的 JSONP函数

查看打包后的代码,如下图,关键在于执行 push 函数 在 qiankun 中 document.head.appendChild 方法影响到 webpack 代码分割的问题

至此,文件加载完成,代码分割结束

回到我们最初的问题

我们的完整的流程是这样的

初始化先加载的是主应用

然后手动加载子应用,qiankun 内部对 js 和 window 进行隔离

子应用初始化 webpack 代码分割的 window.JSONP函数

注意:此时的 JSONP 函数是挂在了被 qiankun 做了隔离的 window 上,

document.head.appendChild 恢复成原生方法

注:为了实现 js隔离 和 window隔离,qiankun 内部会 重写document.head.appendChild 方法

触发子应用内的代码分割,webpack 执行 document.head.appendChild() 方法,因为已经恢复成了原生方法,所以 script 标签会被加载到 dom 上,此时 分割后的代码运行在主应用的 window 环境下

上文提到,qiankun 加载子应用的时候会对 js 和 window 进行隔离

这就导致 webpack 没有找到应该执行的 window.JSONP 函数,进而 webpack 内部报错

至此,整个流程分析完毕

qiankun 内部相关逻辑

在运行子应用的时候,如果遇到代码分割

qiankun 不会讲 script 标签加载到dom上,如果这样做,那么就失去了隔离的意义

qiankun 重写了 document.head.appendChild 方法

上文提到 webpack 在运行时执行 document.head.appendChild 方法,这个时候执行的其实是被 qiankun 重写的的方法

获取到 script 标签上的 src ,通过 fetch 发情请求加载js文件,之后会在沙箱内执行这个文件内的js代码,再之后触发自定义的 load 事件,进而触发 webpack 内的 script.onload 方法,完成代码分割的后续流程

const loadEvent = new CustomEvent('load')

element.onload(patchedEvent) // 触发 webpack 内的 script.onload

在沙箱内执行js代码 在 qiankun 中 document.head.appendChild 方法影响到 webpack 代码分割的问题

总结

由于我们将 document.head.appendChild 恢复为原生方法,导致 webpack 找不到了应该执行的 JSONP函数,进而导致 webpack 内部报错