18

@babel/preset-env对于与结合useBuiltIns使用的不同配置,我得到不同的输出@babel/transform-runtime。我已阅读文档,但无法弄清楚最佳实践应该是什么。

例如,当我的目标浏览器列表包括 Edge 18 时, @babel/preset-envwithuseBuiltIns将添加一个 polyfill 。string.replace

但是当我使用它时@babel/transform-runtime,不会添加那个 polyfill。


所以,从这个问题开始:

Does `string.replace` need to be polyfilled for Edge 18?

caniuse,mdn并且compat-table是很好的教育资源,但并不是真正打算用作开发人员工具的数据源:仅compat-table包含一组良好的 ES 相关数据,并且由 @babel/preset-env 使用,但它有一些限制

并进一步:

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

所以我将我的目标浏览器传递给core-js-compat它,它会输出所有需要的 polfills。如下图所示,很多字符串方法需要填充,主要是为了支持 Edge 18。

在此处输入图像描述

到目前为止,一切都很好。看起来string.replace 确实需要为 Edge 18 填充。


通天塔配置

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

当我使用useBuiltIns: 'usage'从以下位置引入每个文件的 polyfill 时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文件中添加以下 polyfill:

// 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的是 target edge: 17,而不是edge: 18我们在core-js-compat上面的输出中看到的 - 可能是我已经做过的事情,但现在没问题。

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/runtimewithcorejs: 3选项简化了使用 core-js-pure. core-js 它自动将 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.*polyfills 的迹象。他们不再需要了吗?他们是不是已经被“帮手”带进来了?它看起来不像对象扩展和数组映射与填充字符串实例方法有任何关系,所以我认为没有。


最后

我最后一次尝试是结合这两种方法 - 并遵循建议

在此处输入图像描述

a) 设置corejs@babel/preset-env

// 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) 设置corejs@babel/transform-runtime

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

比较不同方法的输出

仅使用useBuiltIns

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

仅使用@babel/runtime-transform

  • 不引入任何字符串 polyfills,但引入了其他助手/polyfills ??,用于数组映射和对象传播

结合使用useBuiltIns@babel/transform-runtime

  • 引入所需的字符串 polyfill,但会污染全局命名空间。
  • 还引入了 Object spread polyfill(但不是 Array map polyfill)
  • @babel/runtime/helpers/objectSpread2,而不是@babel/runtime-corejs3/helpers/objectSpread2(运行时与运行时-corejs3)导入 - 可能是未引入 Array map polyfill 的原因??)

问题

哪一个(如果有的话)是正确的方法?

我猜@babel/preset-envwithuseBuiltIns是最好的,因为它引入了 polyfills。

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

结合@babel/transform-runtime,我们还获得了一个用于对象传播的 polyfill(尽管@babel-preset-envcorejs: { version: '3.6', proposals: true }which should polyfill 提议,所以我不确定为什么不使用@babel/transform-runtime插件就不能将它引入那里)

我们需要 Array#map polyfill 吗?

4

1 回答 1

11

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

App:如果您正在编写应用程序,请在应用程序顶部使用 import 'core-js 并将 useBuiltIns 设置为 entry ,并将 @babel/transform-runtime 仅用于帮助程序(@babel/runtime 作为依赖项)。这样你污染了全球环境,但你不在乎,它是你的应用程序。您将受益于别名为 @babel/runtime 的助手和包含在应用顶部的 polyfill。这样,您也不需要处理 node_modules(除非依赖项使用必须转换的语法),因为如果某些依赖项使用了需要 polyfill 的功能,则您已经将该 polyfill 包含在应用程序的顶部。

:如果您正在编写库,请仅使用带有 corejs 选项的 @babel/transform-runtime 加上 @babel/runtime-corejs3 作为依赖项,并使用 @babel/preset-env 使用 useBuiltIns: false 进行语法转换。此外,我会从 node_modules 转译我将使用的包。为此,您需要设置 absoluteRuntime 选项(https://babeljs.io/docs/en/babel-plugin-transform-runtime#absoluteruntime)以从一个地方解决运行时依赖关系,因为 @babel/transform-runtime直接从 @babel/runtime-corejs3 导入,但仅当 @babel/runtime-corejs3 在正在编译的文件的 node_modules 中时才有效。

更多信息:

于 2021-01-13T13:21:17.750 回答