问题
开门见山,你可能会在 qiankun 等微前端体系中,有多个子应用时,发生这样的加载崩溃问题:
Cannot redefine property: DEG_PER_RAD
Cannot redefine property: RAD_PER_DEG
实际上 DEG_PER_RAD
和 RAD_PER_DEG
都是 Math
上的静态常量,在以下的描述中,我们就以 Math.DEG_PER_RAD
为例进行拆解。
原因拆解
崩溃的根本原因是,core-js
在 3.23.0 做出了一个改动:不能对 Math.DEG_PER_RAD
重复赋值( 相关 issue 详见 #1091 )。
在 core-js 3.23.0
将 Math.DEG_PER_RAD
设定为了 nonConfigurable: true
且 nonWritable: true
的( 代码详见 esnext.math.deg-per-rad.js ),所以当加载了 >= 3.23.0
的 polyfill 后,不能对 Math.DEG_PER_RAD
重新写入了,此时再加载 < 3.23.0
的 polyfill ,就会发生崩溃报错。
而先加载了 < 3.23.0
的 polyfill ,再加载 >= 3.23.0
的话,是没问题的。
为什么常会发生在微前端下?
通过问题拆解,我们发现此报错是与 core-js
不同版本的加载 顺序 强相关的,而在 qiankun 类似的微前端体系下,往往每个子应用都会有一份 polyfill ,所以他们可能会发生本文中的致命冲突,导致应用彻底崩溃。
你可以在全局变量 window['__core-js_shared__']
中看到当前项目中有多少 core-js
版本:
解法
这里我们先不进一步探究,直接提供解法:
要解的无非是 Math.DEG_PER_RAD
和 Math.RAD_PER_DEG
的问题,那我们直接排除这两个 polyfill 就可以了,他们在 core-js
中的名字是 esnext.math.deg-per-rad
和 esnext.math.rad-per-deg
。
如何排除?
对于社区中大部分的脚手架来说,他们的 babel polyfill 策略都是 usage
,所以当你不使用这两个 Math.xxx
常量的时候他是不会引入 polyfill 的。
既然发生了此问题,说明你的 polyfill 大概率是 entry
策略(此处不再展开讲解 babel polyfill 策略),是全量引入的,所以你应该找到相关源头,排除掉他们。
由于排除的手段和 框架
/ 脚手架
实现逻辑强相关,所以本文不做具体描述,在排除过程中,除了可以定点排除 esnext.math.deg-per-rad
和 esnext.math.rad-per-deg
外,考虑到未来如果新增其他的 Math.constant
常量,也要再排除,同时 esnext.math.xxx
都是 stage 1 的 polyfill (详见 proposal-math-extensions ),几乎不会有人使用到,全部排除了也是没什么风险的,故可以把 esnext.math.xxx
全部过滤掉。
当你无法触及、或不了解框架行为,不能自行解决时,可以考虑到相关框架、脚手架的 issue 区反馈,寻求维护者的帮助。
swc 的 polyfill 策略排除法
为了讲解的更全面,此处我们还提供 swc 的 polyfill 排除解法。对于 swc 来说,我们推荐使用的 polyfill 策略是人工指定 includes
方法(因为 swc 的 usage
策略速度慢,且更新不及时),详见:
- 《 swc兼容polyfill的权衡与选择 》
那在获取 polyfill 列表结束后,人工过滤掉 esnext.math.xxx
的 polyfill 即可:
// 过滤部分逻辑
const filteredList = (list as string[]).filter(line => {
// https://github.com/zloirock/core-js/issues/1091
// Prevent `Math.DEG_PER_RAD` / `Math.RAD_PER_DEG` constant override problem in qiankun micro app
return !line.startsWith('esnext.math.')
})
其他可能的解法
除了从 core-js
本身上来解决该问题,这里再提供一些其他的解法思路:
-
将主应用、子应用全部升级到最新的
core-js
版本,避开含有< 3.23.0
的core-js
情况,可解此问题。 -
因为主应用含有 polyfill ,子应用没有独立使用的场景时,无需 polyfill ,把子应用的 polyfill 关掉就可以了。
总结
core-js
本身是没问题的,只不过你加载了多份 polyfill ,那就说不准了,这就是:微前端路漫漫其修远兮,只有踩过了坑才知道事出不易。
由于本文探讨的问题场景比较特定和深入,更多的面向对 webpack 比较了解的用户,跳过了很多基础概念的介绍,希望对你有所帮助。