场景还原

最近将一个项目由babel@6升级到babel@7,升级后最重要的两个包:

  • @babel/preset-env: 提供代码的转换和API的polyfill的能力
  • @babel/plugin-transform-runtime: 复用babel注入的helper代码以及提供无污染全局环境的polyfill功能

基于此,对项目中js语法的transform和API的polyfill进行了调整:

  • 关闭@babel/plugin-transform-runtime的polyfill功能
  • 开启@babel/preset-env的polyfill和transform功能

其中,@babel/preset-env的polyfill使用usage形式(不了解的可以查看官方文档),意思是以项目设置的target环境为前提,根据项目中使用到的api功能进行polyfill;具体babel配置片段如下:

{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": false,
"regenerator": false
}
]
],
"sourceType": "unambiguous",
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"shippedProposals": true,
"useBuiltIns": "usage",
"corejs": {
"version": "3.10",
"proposals": true
},
"targets": {
...
}
}
]
]
}

然后项目中使用到了Promise.allSettled静态方法:

Promise.allSettled([p1, p2, p3]).then(res => console.log(res));

通过webpack打包后运行,js会报错:

TypeError: Promise.allSettled is not a function

不对呀,按照官网就是这么配置的,一度对babel的配置产生怀疑,折腾半天最后都排除掉;没招了,那就试试断点调试,别说还真发现问题,直接上图:

相信大家能够看出问题所在,Promise.allSettled的polyfill之后重新引入Promise的polyfill,后面的Promise的polyfill覆盖了Promise.allSettled的polyfill,导致调用该方法时报错。

那会不会是babel的bug导致的呢,于是开起查找问题之旅了。。。

问题追踪

首先,简要说明下@babel/preset-env实现polyfill的思路:babel会生成代码的ast,并对其traverse过程中,根据代码使用的新API来确定需要填充的polyfill。

遇到这种问题,首先想到会不会是@babel/preset-env的bug,google半天也没有找到类似问题,于是就开启debug调试模式。在调试追踪到babel-plugin-polyfill-corejs3/lib/index.js中的usageGlobal方法,其在解析代码中使用到了PromiseallSettled的api,如下图:

babel会根据代码用到的api,最终解析出为这些api注入的polyfill,如下图:

从图可以看出最终需要为PromiseallSettled注入的依赖polyfill;但是注入的polyfill存在问题,即es.promisees.promise.all-settled顺序反了,后者依赖前者;由此可见是babel的bug已确定无疑了。

接着进如resolve方法,发现其在确定代码的相关polyfill依赖后,对与依赖的先后顺序存在bug;因为代码调用Promise.allSettled会依赖:

  • 全局global的Promise api
  • Promise的静态方法allSettled api

所以babel在获取二者对应的polyfill在合并时产生了问题,这可以在babel-plugin-polyfill-corejs/lib/built-in-definitions.js文件中:

// 所有静态方法的polyfill
const StaticProperties = {
...
Promise: {
all: define(null, PromiseDependenciesWithIterators),
allSettled: define(null, ["es.promise.all-settled", ...PromiseDependenciesWithIterators]),
any: define(null, ["esnext.promise.any", ...PromiseDependenciesWithIterators]),
race: define(null, PromiseDependenciesWithIterators),
try: define(null, ["esnext.promise.try", ...PromiseDependenciesWithIterators])
},
...
}

可以看出Promise的相关静态方法的polyfill都放置到第一位,而define为对该数值进行任何排序:

const define = (pure, global, name = global[0], exclude) => {
return {
name,
pure,
global,
exclude
};
};

查到这里可以猜测这个babel-plugin-polyfill-corejs3@0.1.7有bug,查看最新版本0.2.0的代码发现对这个方法进行了修复:

var _data = _interopRequireDefault(require("../core-js-compat/data.js"));

const polyfillsOrder = {};
Object.keys(_data.default).forEach((name, index) => {
polyfillsOrder[name] = index;
}); const define = (pure, global, name = global[0], exclude) => {
return {
name,
pure,
global: global.sort((a, b) => polyfillsOrder[a] - polyfillsOrder[b]),
exclude
};
};

可以看出该方法对注入的polyfill做了排序,进过排序得到正确的依赖顺序,于是果断升级@babel/preset-env@7.13.15,因为之前@babel/preset-env@7.13.10依赖的是babel-plugin-polyfill-corejs3@0.1.7,至此一直困扰我的这个大坑给堵上了。

出于好奇心,对babel-plugin-polyfill-corejs3代码进行blame,果然发现这个问题在24天前进行了修复:

进一步查看发现,之前已经有人提出过类似的bug:The order of promise and promise.finally after compilation seems to be wrong,于是做了修复。

总结

困扰我一天的问题算是解决了,分享给大家希望大家避坑。

不过话说回来,开始遇到这个问题时,换成@babel/preset-enventry模式的polyfill模式不会发生任何问题,但是心中过不去这个坎为啥usage模式不能用,明明后者有一定的体积优势,最终得到答案;这一过程虽然耗费一定的时间,但是有收获,值!

@babel/preset-env使用polyfill遇到的坑的更多相关文章

  1. Error: Couldn't find preset "env" relative to directory "/Users/user/ethereumjs-vm"

    运行npm run build时遇见这个问题,解决办法是安装: npm install --save-dev babel-preset-env 就解决了

  2. vue客户端渲染首屏优化之道

    提取第三方库,缓存,减少打包体积 1. dll动态链接库, 使用DllPlugin DllReferencePlugin,将第三方库提取出来另外打包出来,然后动态引入html.可以提高打包速度和缓存第 ...

  3. babel-preset-env: a preset that configures Babel for you

    转载 babel-preset-env is a new preset that lets you specify an environment and automatically enables t ...

  4. babel更新之后的 一些坑

    最近在使用babel-loader的时候,发生了一些错误,现在的babel-loader版本已经是8.0.0,更新到这个版本之后,如果还按照以前的安装依赖的方法: cnpm install --sav ...

  5. 《前端之路》之 Babel 下一代 JavaScript 语法编译器

    写本章的内容的出发点主要是 为了对于之前关于 JS 版本的一个总结,在之前的开发中,我们始终对于 ECMAScript 的版本的更新不够重视,以至于在后面的 开发过程中,我们始终会被各种新奇的语法打断 ...

  6. babel 7 简单升级指南

    babel 7 babel 7 发布两天了,试着对当前项目更新了下,仅此记录分享 主要改动参考 官方博客 官方升级指南 主要升级内容 不再支持放弃维护的 node 版本 0.10.0.12.4.5 使 ...

  7. Babel总结

    什么是babel? babel是一个JavaScript编译器. Babel是一个工具链,主要用于将ECMAScript 2015+代码转换为向后兼容的旧浏览器或环境中JavaScript版本. 注解 ...

  8. 前端利器躬行记(2)——Babel

    Babel是一个JavaScript编译器,不仅能将当前运行环境不支持的JavaScript语法(例如ES6.ES7等)编译成向下兼容的可用语法(例如ES3或ES5),这其中会涉及新语法的转换和缺失特 ...

  9. 前端自动化之babel本地化安装

    npm添加package.json cd到项目根目录直接调用npm init 会创建package.json文件 本地安装bebel(并非全局安装,这种情况下cmd命令中babel命令不识别): 步骤 ...

随机推荐

  1. 12月17日BGV币行情分析

    目前BGV收于353.95美金,较前一交易日上涨28.25%. 非小号数据显示,BGV最大客户aofexpay.ngk出现+490.2349的持币变化,其次减仓数额均不大,分别为-80.1,-30,- ...

  2. 谈谈 JS 垃圾回收机制

    谈谈 JS 垃圾回收机制 JS内存泄漏与垃圾回收机制 https://javascript.info/garbage-collection

  3. Golang 实现 Redis(9): 使用GeoHash 搜索附近的人

    本文是使用 golang 实现 redis 系列的第九篇,主要介绍如何使用 GeoHash 实现搜索附近的人. 搜索附近的POI是一个非常常见的功能,它的技术难点在于地理位置是二维的(经纬度)而我们常 ...

  4. Linux 网络分析必备技能:tcpdump 实战详解

    大家好,我是肖邦,这是我的第 11 篇原创文章. 今天要分享的是 tcpdump,它是 Linux 系统中特别有用的网络工具,通常用于故障诊断.网络分析,功能非常的强大. 相对于其它 Linux 工具 ...

  5. vue项目配置 `webpack-obfuscator` 进行代码加密混淆

    背景 公司代码提供给第三方使用,为了不完全泄露源码,需要对给出的代码进行加密混淆,前端代码虽然无法做到完全加密混淆,但是通过使用 webpack-obfuscator 通过增加随机废代码段.字符编码转 ...

  6. Java(JDK/Tomcat/Maven)运行环境配置及工具(idea/eclipse)安装

    Java (计算机编程语言) Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,因此Java语言具有功能强大和简单易用两个特征. Java语 ...

  7. ClickHouse源码笔记4:FilterBlockInputStream, 探寻where,having的实现

    书接上文,本篇继续分享ClickHouse源码中一个重要的流,FilterBlockInputStream的实现,重点在于分析Clickhouse是如何在执行引擎实现向量化的Filter操作符,而利用 ...

  8. AntDesign Pro + .NET Core 实现基于JWT的登录认证

    很多同学说AgileConfig的UI实在是太丑了.我想想也是的,本来这个项目是我自己使用的,一开始甚至连UI都没有,全靠手动在数据库里修改数据.后来加上了UI也是使用了老掉牙的bootstrap3做 ...

  9. POJ-2387(原始dijkstra求最短路)

    Til the Cows Come Home POJ-2387 这题是最简单的最短路求解题,主要就是使用dijkstra算法,时间复杂度是\(O(n^2)\). 需要注意的是,一定要看清楚题目的输入要 ...

  10. 基于Hi3559AV100 RFCN实现细节解析-(3)系统输入VI分析(HiISP)二 :

    下面随笔系列将对Hi3559AV100 RFCN实现细节进行解析,整个过程涉及到VI.VDEC.VPSS.VGS.VO.NNIE,其中涉及的内容,大家可以参考之前我写的博客: 基于Hi3559AV10 ...