babel 到底需不需要在入口引入 regenerator-runtime

介绍

看过 babel 文档的小伙伴都知道,在介绍 @babel/polyfill 的章节中,有这样一段描述:

2023-06-02-09-43-48.png

原文:

? 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

步骤

  1. 新建目录 test1。

  2. 创建 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"
  }
}
  1. 复制一份 index.js 在目录下
  2. 创建 babel.config.js 文件
    因为我们只需要编译 async 函数,所以不用配其它插件。
module.exports = {
  plugins: [
    '@babel/plugin-transform-regenerator'
  ]
}
  1. npm i 下载依赖
  2. 执行 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,希望能改正这个错误。很快,我就得到了回复:

2023-06-02-10-25-49.png

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

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYSUvpGv' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片