`@babel/preset-env`+`useBuiltIns`+`@babel/runtime`+`Browserlistrc`的最佳实践是什么

2022-02-25 00:00:00 javascript babeljs babel-preset-env
useBuiltIns@babel/transform-runtime结合使用时,@babel/preset-env的不同配置得到不同的输出。我已阅读文档,但无法确定最佳做法应该是什么。

例如,当我的目标浏览器列表包括Edge 18时,@babel/preset-envWITHuseBuiltIns将为string.replace添加一个多边形填充。

但是当我改用@babel/transform-runtime时,不会添加该填充。


那么,从这个问题开始:

Does `string.replace` need to be polyfilled for Edge 18?
  • 我检查了caniuse.com,结果显示它完全受支持-这意味着不需要填充。

  • 然而,根据Manuel Beaudru的博客帖子core-js@3, babel and a look into the future

caniusemdncompat-table是很好的教育资源,但是 实际上并不是要用作开发人员工具的数据源: 只有compat-table包含一组良好的ES相关数据,并且它 由@babel/preset-env使用,但有一些限制

并进一步:

为此,我创建了core-js-compat包:它提供 关于不同目标的core-js模块必要性的数据 引擎。使用core-js@3时,@babel/preset-env将使用该新的 包而不是compat-table.

所以我将我的目标浏览器传递给core-js-compat,它输出所需的所有策略填充。正如您在下图中看到的,相当多的字符串方法需要进行多填充,主要是为了支持Edge 18。

到目前为止,一切顺利。看起来string.replace是否需要为边缘18多填充。


巴别塔配置

第一种方法:@babel/preset-envuseBuiltIns: 'usage'

当我使用useBuiltIns: 'usage'core-js引入每个文件的多边形填充时:

// babel.config.js

  presets: [
    [
      '@babel/preset-env',
      {
        debug: false,
        bugfixes: true,
        useBuiltIns: 'usage',
        corejs: { version: "3.6", proposals: true }
      }
    ],
    '@babel/preset-flow',
    '@babel/preset-react'
  ],

debug: true时,Babel表示会将以下多边形填充添加到我的PriceColumn.js文件中:

// Console output

[/price-column/PriceColumn.js] Added following core-js polyfills:

  es.string.replace { "edge":"17", "firefox":"71", "ios":"12", "safari":"12" }
  es.string.split { "edge":"17" }
  web.dom-collections.iterator { "edge":"17", "ios":"12", "safari":"12" }

一个区别是它说es.string.replace是以edge: 17为目标,而不是我们在上面core-js-compat的输出中看到的edge: 18-可能是我做过的事情,但目前还可以。

Babel添加到转储的PriceColumn.js文件顶部的内容:

// PriceColumn.js

"use strict";

require("core-js/modules/es.string.replace");

require("core-js/modules/es.string.split");

require("core-js/modules/web.dom-collections.iterator");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

再次声明,到目前为止一切正常。


第二种方法:@babel/runtime@babel/transform-runtime

根据core-js文档:

@babel/runtimecorejs: 3选项简化了使用 core-js-pure。它会自动取代现代功能的使用 从JS标准库到core-js版本的导入 无全局命名空间污染

听起来不错-我们试试吧!

注释掉useBuiltIns并添加@babel/transform-runtime插件配置:

// babel.config.js

  presets: [
    [
      '@babel/preset-env',
      {
        debug: true,
        // bugfixes: true,
        // useBuiltIns: 'usage',
        // corejs: { version: '3.6', proposals: true }
      }
    ],
    '@babel/preset-flow',
    '@babel/preset-react'
  ],
  plugins: [
    [
      '@babel/transform-runtime',
      {
        corejs: { version: 3, proposals: true },
        version: '^7.8.3'
      }
    ]
  ],

在控制台输出中,我看到:

Using polyfills: No polyfills were added, since the `useBuiltIns` option was not set.

正在检查添加到文件顶部的内容:

// PriceColumn.js

"use strict";

var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");

var _Object$defineProperty = require("@babel/runtime-corejs3/core-js/object/define-property");

_Object$defineProperty(exports, "__esModule", {
  value: true
});

exports.default = void 0;

var _objectSpread2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/objectSpread2"));

var _map = _interopRequireDefault(require("@babel/runtime-corejs3/core-js/instance/map"));
因此,添加了不同的helpers-但没有显示es.string.*多重填充的迹象。它们不再是必需的了吗?他们已经被"帮手"带进来了吗?看起来对象扩散和数组映射与多填充字符串实例方法没有任何关系,所以我认为没有关系。


最终

我的上一次尝试是将两种方法结合起来-并遵循recommendations:

a)为@babel/preset-env设置corejs

// babel.config.js

  presets: [
    [
      '@babel/preset-env',
      {
        debug: true,
        // bugfixes: true,
        useBuiltIns: 'usage',
        corejs: { version: '3.6', proposals: true }
      }
    ],
    '@babel/preset-flow',
    '@babel/preset-react'
  ],
  
  plugins: [
    [
      '@babel/transform-runtime',
      {
        // corejs: { version: 3, proposals: true },
        version: '^7.8.3'
      }
    ]
  ]

以下是输出:

// PriceColumn.js

"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

require("core-js/modules/es.string.replace");

require("core-js/modules/es.string.split");

require("core-js/modules/web.dom-collections.iterator");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));

b)为@babel/transform-runtime设置corejs

  • 与第二种方法相同(见上文)

比较不同方法的输出

仅使用useBuiltIns

  • 引入所需的字符串多填充,但会污染全局命名空间。

仅使用@babel/runtime-transform

  • 不引入任何字符串多边形填充,但为数组贴图和对象扩散引入其他辅助对象/多边形填充??

同时使用useBuiltIns@babel/transform-runtime

  • 引入所需的字符串多填充,但会污染全局命名空间。
  • 还会引入对象扩散多边形填充(而不是数组贴图多边形填充)
  • @babel/runtime/helpers/objectSpread2导入,而不是@babel/runtime-corejs3/helpers/objectSpread2(运行时vs运行时-corejs3)-可能是未引入数组贴图多边形填充的原因??)

问题

以下哪种(如果有)是正确的方法?

我猜@babel/preset-envuseBuiltIns是最好的,因为它引入了多边形填充。

污染全局命名空间有什么缺点?这只是库的问题吗?

@babel/transform-runtime结合使用,我们还会获得对象展开的多边形填充(尽管@babel-preset-envcorejs: { version: '3.6', proposals: true }应该是多边形填充建议,所以我不确定为什么不使用@babel/transform-runtime插件就不将其引入其中)

是否需要Array#贴图多边形填充?


解决方案

建议https://www.jmarkoski.com/understanding-babel-preset-env-and-transform-runtime:

应用程序:如果您正在创作应用程序,请在应用程序的顶部使用import‘core-js,useBuiltIns设置为Entry,@Babel/Transform-Runtime仅用于助手(@babel/runtime作为依赖项)。这样你污染了全球环境,但你不在乎,这是你的应用程序。您将受益于应用程序顶部包含的别名为@babel/run和PolyFill的帮助器。这样,您也不需要处理NODE_MODULES(除非依赖项使用了必须转换的语法),因为如果某些依赖项使用了需要多边形填充的功能,则您已经在应用程序的顶部包含了该多边形填充。

库:如果您正在创作库,请仅使用带有corejs选项的@babel/Transform-runtime加上@babel/run-corejs3作为依赖项,并使用@babel/preset-env进行带有useBuiltIns:false的语法转换。此外,我还将传输我将从node_module使用的包。为此,您将需要设置AbsoluteRuntime选项(https://babeljs.io/docs/en/babel-plugin-transform-runtime#absoluteruntime)以从单个位置解析运行时依赖项,因为@babel/Transform-Runtime直接从@babel/run-corejs3导入,但这仅在@babel/run-corejs3位于正在编译的文件的node_module中时才有效。

更多信息:

  • https://github.com/zloirock/core-js/issues/848
  • https://github.com/zloirock/core-js/issues/833
  • https://babeljs.io/docs/en/babel-plugin-transform-runtime

相关文章