electron项目升级了webpack5,项目跑起来发现报错,regeneratorruntime is not defined,项目无法正常运行 😂

报错原因

这个报错是由于 async function 语法糖被 babel 转译之后使用了 regeneratorRuntime 这个变量,但是这个变量在最终的代码里未定义造成的报错。

情景复现

我们新建一个文件,使用 async 语法来编写代码

// index.js
async function foo() {
  const ret = await Promise.resolve(2);
  console.log(ret);
}
foo();

配置 webpack.config.js 

// webpack.config.js
module.exports = {
  entry: './index.js',
  output: {
    pathpath.resolve(__dirname, 'dist'),
    filename: 'index.bundle.js',
  }
};

运行 webpack 后打包出的结果如下

!async function () { const o = await Promise.resolve(2); console.log(o) }();

为了兼容低版本浏览器,我们再引入 babel 

module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  }
};

配置 .babelrc 如下

{
  "presets": [
    [
      "@babel/preset-env"
    ]
  ]
}

打包的结果如下

!function(){function n(n,e,r,t,o,i,u){try{var c=n[i](u),a=c.value}catch(n){return void r(n)}c.done?e(a):Promise.resolve(a).then(t,o)}function e(e){return function(){var r=this,t=arguments;return new Promise((function(o,i){var u=e.apply(r,t);function c(e){n(u,o,i,c,a,"next",e)}function a(e){n(u,o,i,c,a,"throw",e)}c(void 0)}))}}function r(){return(r=e(regeneratorRuntime.mark((function n(){var e;return regeneratorRuntime.wrap((function(n){for(;;)switch(n.prev=n.next){case 0:return n.next=2,Promise.resolve(2);case 2:e=n.sent,console.log(e);case 4:case"end":return n.stop()}}),n)})))).apply(this,arguments)}!function(){r.apply(this,arguments)}()}();

运行一下打包 index.bundle.js 文件,发现报错了,错误如下图

因为 babel 在转 async 语法的时候,使用 regeneratorRuntime 这个变量。然后这个变量未声明,所以报错了。

解决办法

1. 配置 @babel/preset-env 的 targets 选项

如果我们确定代码运行在兼容 async 语法的环境下,可以配置 node 的版本,这样 babel 在转换的时候就不会去处理 async 语法的代码

{
  "presets": [
    [
      "@babel/preset-env", {
        "targets": {
          "node": "15.4"
        }
      }
    ]
  ]
}

结果等同于上面不配置 babel 的情况

!async function () { const o = await Promise.resolve(2); console.log(o) }();

2. 配置 @babel/plugin-transform-runtime (推荐)

{
  "presets": ["@babel/preset-env"],
  "plugins": [
    "@babel/plugin-transform-runtime"
  ]
}

3. 配置 @babel/preset-env 的 useBuiltIns (不推荐)

如果使用 usage 或者 entry ,将会使用 core-js 模块来处理,默认使用 2.0 版本,我们这里安装3.0 npm i core-js@3.17.3,然后手动声明 corejs 版本

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "corejs": "3.17.3"
      }
    ]
  ]
}

使用 useBuiltIns 可能会修改全局变量或者全局对象的原型链,如果多个库同时修改同一变量的情况下就会导致难以跟踪的问题 https://github.com/babel/babel/issues/10271#issuecomment-5283795