介绍
看过 babel 文档的小伙伴都知道,在介绍 @babel/polyfill 的章节中,有这样一段描述:
原文:
? As of Babel 7.4.0, this package has been deprecated in favor of directly including core-js/stable (to polyfill ECMAScript features):
import "core-js/stable";
If you are compiling generators or async function to ES5, and you are using a version of @babel/core or @babel/plugin-transform-regenerator older than 7.18.0, you must also load the regenerator runtime package. It is automatically loaded when using @babel/preset-env’s useBuiltIns: “usage” option or @babel/plugin-transform-runtime.
用我的话解释一下:
自从 babel@7.4.0 开始,这个包(@babel/polyfill)已经过时了,更好的做法是引入 core-js/stable
。
如果你正打算把 generator 函数或则 async 函数编译为 ES5,但是如果你使用的这两个包(指的是 @babel/core 和 @babel/plugin-transform-regenerator)的版本有其中一个小于 7.18.0,那么你就得在入口文件加载 regenerator-runtime 运行时。
换句话说,只有这两个包的版本同时大于 7.18.0,你才不用像下面这样在入口导入 regenerator-runtime。
import "regenerator-runtime/runtime.js";
测试
假设我们有这样一个 index.js 文件,里面写了 async 函数。我们要将其转为 ES5,然后用 node 执行这个文件。
main()
async function main() {
await new Promise((resolve) => {
setTimeout(() => {
console.log('hello')
resolve()
}, 1000)
})
}
要求
- @babel/core 版本小于
7.18.0
- @babel/plugin-transform-regenerator 版本大于
7.18.0
步骤
-
新建目录 test1。
-
创建 package.json
注意 @babel/core 和 @babel/plugin-transform-regenerator 依赖版本已写死。
{
"name": "test1",
"scripts": {
"compile": "babel index.js -o compiled.js"
},
"devDependencies": {
"@babel/cli": "^7.20.7",
"@babel/core": "7.16.7",
"@babel/plugin-transform-regenerator": "7.21.5"
}
}
- 复制一份 index.js 在目录下
- 创建 babel.config.js 文件
因为我们只需要编译 async 函数,所以不用配其它插件。
module.exports = {
plugins: [
'@babel/plugin-transform-regenerator'
]
}
npm i
下载依赖- 执行
compile
脚本,查看编译后的 compiled.js 文件
发现编译后的文件内联了 _regeneratorRuntime 函数,此处我们用 node 执行该文件,一秒后在控制台会打印 hello。
function _regeneratorRuntime() { "use strict";
// 略 ...
}
main();
function main() {
return _regeneratorRuntime().async(function main$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return _regeneratorRuntime().awrap(new Promise(() => {
setTimeout(resolve => {
console.log('hello, babel regenerator runtime');
resolve();
}, 1000);
}));
case 2:
case "end":
return _context.stop();
}
}, null, null, null, Promise);
}
疑惑
这时我就有了疑惑,不是说 @babel/core 或者 @babel/plugin-transform-regenerator 只要其中有一个的版本低于 7.18.0,就需要在入口文件引入 regeneratorRuntime 吗。怎么现在自动引入了内联辅助函数呢?
const regeneratorRuntime = require("regenerator-runtime");
于是我在 babel 官方文档仓库中提了一个 pr,希望能改正这个错误。很快,我就得到了回复:
Thanks for this PR! I agree that the current documentation is not precise, however I think it might be good enough.
The real requirement is “you must have at least version 7.18.0 of @babel/plugin-transform-regenerator and @babel/helpers”. @babel/helpers is a dependency of @babel/core. @babel/core 7.18.0 depends on @babel/helpers 7.18.0 so it’s guaranteed to get a new enough version. However, @babel/core 7.17.12 depends on @babel/helpers ^7.17.9, so it might get an older version.
原来,必须是 @babel/helper 和 @babel/plugin-transform-regenerator 的版本同时大于 7.18.0,才不用引入 regenerator-runtime。
@babel/helper 是 @babel/core 的依赖,虽然我们能写死 @babel/core 的依赖,但并不能保证 @babel/core 的依赖自动升级。
于是,我改了下 package.json 文件,把 @babel/helper 也加入到 devDependencies 字段中。
{
"name": "import-regenerator-runtime-4",
"scripts": {
"compile": "babel index.js -o compiled.js"
},
"devDependencies": {
"@babel/cli": "^7.20.7",
"@babel/core": "7.16.7",
"@babel/plugin-transform-regenerator": "7.21.5",
"@babel/helpers": "7.17.9"
}
}
这会再测试,发现编译后的文件没有引入内联辅助函数了。需要我们在全局手动引入 regenerator-runtime。
main();
function main() {
return regeneratorRuntime.async(function main$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return regeneratorRuntime.awrap(new Promise(resolve => {
setTimeout(() => {
console.log('hello, babel regenerator runtime');
resolve();
}, 1000);
}));
case 2:
case "end":
return _context.stop();
}
}, null, null, null, Promise);
}
总结
官网上不太准确的说法:如果你正在编译 generator 函数或 async 函数为 ES5,并且你正在使用版本低于 7.18.0 的 @babel/core 或 @babel/plugin-transform-regenerator,那么你必须在入口文件引入 regenerator runtime。
准确的说法:如果你正在编译 generator 函数或 async 函数为 ES5,并且你正在使用版本低于 7.18.0 的 @babel/helpers
(@babel/core 下的子依赖) 或 @babel/plugin-transform-regenerator,那么你必须在入口文件引入 regenerator runtime。