`@babel/preset-env` + `useBuiltIns` + `@babel/runtime` + `browserslistrc` 的最佳实践是什么
What is best practice for `@babel/preset-env` + `useBuiltIns` + `@babel/runtime` + `browserslistrc`
对于 @babel/preset-env
的不同配置,useBuiltIns
与 @babel/transform-runtime
结合使用,我得到不同的输出。我已阅读文档,但无法弄清楚最佳实践应该是什么。
例如,当我的目标浏览器列表包括 Edge 18 时,@babel/preset-env
和 useBuiltIns
将为 string.replace
添加一个 polyfill。
但是当我改用 @babel/transform-runtime
时,不会添加该 polyfill。
所以,从这个问题开始:
Does `string.replace` need to be polyfilled for Edge 18?
我检查了 caniuse.com,这表明它完全受支持 - 这意味着不需要 polyfill。
但是,根据 Manuel Beaudru 的博客 post core-js@3, babel and a look into the future
caniuse
, mdn
and compat-table
are good educational resources but
aren't really meant to be used as data sources for developer tools:
only the compat-table
contains a good set of ES-related data and it
is used by @babel/preset-env, but it has some limitations
还有:
For this reason, I created the core-js-compat
package: it provides
data about the necessity of core-js modules for different target
engines. When using core-js@3
, @babel/preset-env
will use that new
package instead of compat-table.
所以我将我的目标浏览器传递给 core-js-compat
并且它输出所有需要的 polfills。正如您在下图中看到的,需要填充相当多的字符串方法,主要是为了支持 Edge 18。
到目前为止,还不错。看起来 string.replace
需要为 Edge 18 进行 polyfill。
Babel 配置
第一种方法:@babel/preset-env
和 useBuiltIns: 'usage'
当我使用 useBuiltIns: 'usage'
从 core-js
引入每个文件的 polyfill 时:
// 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 说它会将以下 polyfill 添加到我的 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/runtime
with corejs: 3
option simplifies work with
core-js-pure
. It automatically replaces usage of modern features
from JS standard library to imports from the version of core-js
without global namespace pollution
听起来不错 - 让我们试试吧!
注释掉 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.*
polyfill 的迹象。不再需要它们了吗?他们已经被 'helpers' 引入了吗?看起来对象传播和数组映射与 polyfilling 字符串实例方法没有任何关系,所以我认为不是。
终于
我最后一次尝试是结合这两种方法 - 并遵循 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
:
- 引入所需的字符串 polyfill,但污染了全局命名空间。
仅使用 @babel/runtime-transform
:
- 不引入任何字符串 polyfill,但引入其他 helpers/polyfills ??,用于 Array map 和 Object spread
同时使用 useBuiltIns
和 @babel/transform-runtime
:
- 引入所需的字符串 polyfill,但污染了全局命名空间。
- 还引入了 Object spread polyfill(但不是 Array map polyfill)
- 从
@babel/runtime/helpers/objectSpread2
导入,而不是 @babel/runtime-corejs3/helpers/objectSpread2
(运行时 vs runtime-corejs3)- 可能是没有引入 Array map polyfill 的原因??)
问题
以下哪种方法(如果有的话)是正确的方法?
我猜 @babel/preset-env
和 useBuiltIns
是最好的,因为它引入了 polyfill。
污染全局命名空间有什么弊端?这仅是图书馆的问题吗?
结合@babel/transform-runtime
,我们还得到了对象传播的polyfill(尽管@babel-preset-env
有corejs: { version: '3.6', proposals: true }
应该polyfill提案,所以我不确定为什么它没有无需使用 @babel/transform-runtime
插件即可进入)
我们需要 Array#map polyfill 吗?
https://www.jmarkoski.com/understanding-babel-preset-env-and-transform-runtime 推荐:
App:如果您正在编写应用程序,请在应用程序顶部使用 import 'core-js 并将 useBuiltIns 设置为入口,@babel/transform-runtime 仅用于助手(@babel/runtime 作为依赖项)。这样你污染了全球环境但你不在乎,它是你的应用程序。您将受益于别名为 @babel/runtime 的助手和包含在应用程序顶部的 polyfill。这样您也不需要处理 node_modules (除非依赖项使用必须转换的语法)因为如果某些依赖项使用需要 polyfill 的功能,您已经在顶部包含了该 polyfill你的应用程序。
Library:如果你正在创作一个库,只使用 @babel/transform-runtime with corejs option plus @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 中。
更多信息:
对于 @babel/preset-env
的不同配置,useBuiltIns
与 @babel/transform-runtime
结合使用,我得到不同的输出。我已阅读文档,但无法弄清楚最佳实践应该是什么。
例如,当我的目标浏览器列表包括 Edge 18 时,@babel/preset-env
和 useBuiltIns
将为 string.replace
添加一个 polyfill。
但是当我改用 @babel/transform-runtime
时,不会添加该 polyfill。
所以,从这个问题开始:
Does `string.replace` need to be polyfilled for Edge 18?
我检查了 caniuse.com,这表明它完全受支持 - 这意味着不需要 polyfill。
但是,根据 Manuel Beaudru 的博客 post core-js@3, babel and a look into the future
caniuse
,mdn
andcompat-table
are good educational resources but aren't really meant to be used as data sources for developer tools: only thecompat-table
contains a good set of ES-related data and it is used by @babel/preset-env, but it has some limitations
还有:
For this reason, I created the
core-js-compat
package: it provides data about the necessity of core-js modules for different target engines. When usingcore-js@3
,@babel/preset-env
will use that new package instead ofcompat-table.
所以我将我的目标浏览器传递给 core-js-compat
并且它输出所有需要的 polfills。正如您在下图中看到的,需要填充相当多的字符串方法,主要是为了支持 Edge 18。
到目前为止,还不错。看起来 string.replace
需要为 Edge 18 进行 polyfill。
Babel 配置
第一种方法:@babel/preset-env
和 useBuiltIns: 'usage'
当我使用 useBuiltIns: 'usage'
从 core-js
引入每个文件的 polyfill 时:
// 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 说它会将以下 polyfill 添加到我的 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/runtime
withcorejs: 3
option simplifies work withcore-js-pure
. It automatically replaces usage of modern features from JS standard library to imports from the version ofcore-js
without global namespace pollution
听起来不错 - 让我们试试吧!
注释掉 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.*
polyfill 的迹象。不再需要它们了吗?他们已经被 'helpers' 引入了吗?看起来对象传播和数组映射与 polyfilling 字符串实例方法没有任何关系,所以我认为不是。
终于
我最后一次尝试是结合这两种方法 - 并遵循 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
:
- 引入所需的字符串 polyfill,但污染了全局命名空间。
仅使用 @babel/runtime-transform
:
- 不引入任何字符串 polyfill,但引入其他 helpers/polyfills ??,用于 Array map 和 Object spread
同时使用 useBuiltIns
和 @babel/transform-runtime
:
- 引入所需的字符串 polyfill,但污染了全局命名空间。
- 还引入了 Object spread polyfill(但不是 Array map polyfill)
- 从
@babel/runtime/helpers/objectSpread2
导入,而不是@babel/runtime-corejs3/helpers/objectSpread2
(运行时 vs runtime-corejs3)- 可能是没有引入 Array map polyfill 的原因??)
问题
以下哪种方法(如果有的话)是正确的方法?
我猜 @babel/preset-env
和 useBuiltIns
是最好的,因为它引入了 polyfill。
污染全局命名空间有什么弊端?这仅是图书馆的问题吗?
结合@babel/transform-runtime
,我们还得到了对象传播的polyfill(尽管@babel-preset-env
有corejs: { version: '3.6', proposals: true }
应该polyfill提案,所以我不确定为什么它没有无需使用 @babel/transform-runtime
插件即可进入)
我们需要 Array#map polyfill 吗?
https://www.jmarkoski.com/understanding-babel-preset-env-and-transform-runtime 推荐:
App:如果您正在编写应用程序,请在应用程序顶部使用 import 'core-js 并将 useBuiltIns 设置为入口,@babel/transform-runtime 仅用于助手(@babel/runtime 作为依赖项)。这样你污染了全球环境但你不在乎,它是你的应用程序。您将受益于别名为 @babel/runtime 的助手和包含在应用程序顶部的 polyfill。这样您也不需要处理 node_modules (除非依赖项使用必须转换的语法)因为如果某些依赖项使用需要 polyfill 的功能,您已经在顶部包含了该 polyfill你的应用程序。
Library:如果你正在创作一个库,只使用 @babel/transform-runtime with corejs option plus @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 中。
更多信息: