本文发布于 2019-04-15,总结了 babel 社区的工具使用,以及如何合理地进行配置。若是要看结论的话,直接跳到文章最后一节。javascript
@babel/preset-env
@babel/preset-stage-x
(废弃)@babel/polyfill
@babel/runtime
@babel/plugin-transform-runtime
babel-register
babel-node
@babel/preset-env
babel-preset-env 是一系列插件的合集,官方已不用再使用 preset-201x 和 preset-latst 之类的包,env 包能够根据配置自动处理兼容代码。html
文档:babeljs.io/docs/en/bab…java
targets
string | Array<string> | { [string]: string }, defaults to {}
node
针对你的项目指定生成的代码环境。git
能够是字符串:es6
{ "targets": "> 0.25%, not dead" } 复制代码
也能够是对象:github
{ "targets": { "chrome": "58", "ie": "11" } } 复制代码
默认是{}
chrome
{ "targets": {} } 复制代码
其中的环境值能够参考 browserslist 项目。express
targets.esmodules
npm
boolean
。
也能够针对那些支持 ES Module 的浏览器而优化。当指定本选项时,browsers 字段会被忽视。你能够和 <script type="module"></script>
一块儿使用,来生成更小的脚本代码。
请注意: 当指定 esmodules 选项, browsers targets 会被忽视。
{ "presets": [ [ "@babel/preset-env", { "targets": { "esmodules": true } } ] ] } 复制代码
targets.node
string | "current" | true
。
若是要针对当前 node 版本进行编译,能够指定 "node" :true
或 "node":"current"
,它与 "node":process.versions.node
相同。
targets.safari
string | "tp"
。
若是要针对 Safari 的技术预览版进行编译,能够指定“safari”:“tp”。
targets.browsers
(废弃)
string | Array<string>
。
使用 browserslist 选择浏览器的查询(例如:last 2 versions, > 5%, safari tp
)。
注意,browsers 的结果会被 targets
中的显式项覆盖。(此特性通过检验已废弃)
注意:这将在更高版本中删除,而不是直接将
targets
设置为 browserslist 的兼容查询。
{ "targets": { "browsers": { "chrome": "58", "ie": "11" } } } // 等价于,但最新版已经不能用了 { "targets": { "chrome": "58", "ie": "11" } } 复制代码
spec
boolean
, 默认 false
。
为此预设中支持它们的任何插件启用更符合规范但可能更慢的转换。
loose
boolean
, 默认 false
。
为此预设中容许它们的任何插件启用 "loose" 转换。
modules
"amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false, defaults to "auto"
。
启用将 ES6 模块语法转换为其余模块类型。
将此设置为 false
将不会转换模块。
也要注意 cjs
是 commonjs
的别名。
debug
boolean
, 默认 false
。
经过 console.log
输出使用的 targets/plugins 和插件数据中指定的版本信息。
include
Array<string|RegExp>
,默认 []
。
一个老是包含的插件数组。
如下是有效的配置:
@babel/plugin-transform-spread
和不带前缀的 plugin-transform-spread
的写法都支持。es6.map
,es6.set
,或者 es6.object.assign
。能够彻底或部分指定插件名称(或使用 RegExp )。
支持的写法:
string
): "es6.math.sign"string
): "es6.math.*" (解析为全部带 es6.math
前缀的插件)/^transform-.*$/
或者 new RegExp("^transform-modules-.*")
注意,上面的正则对象中的 .
意思是匹配任何字符,而不是实际的 .
字符。 另请注意,匹配任何字符。.*
是在正则中使用,不一样于 *
在 glob
格式中使用。
此选项主要针对于原生加强代码中的 BUG,或者一系列没有起做用的不受支持的功能特性。
例如,node 4 支持原生 class
但不支持 spread
。若是 super
须要 spread
特性,那么须要包含 @babel/plugin-transform-classes
插件。
注意:
include
和exclude
选项仅适用于此预设中包含的插件; 所以,在选项中包含@babel/plugin-proposal-do-expressions
排除或@babel/plugin-proposal-function-bind
会抛出错误(由于此预设没有这些项目)。要使用此预设中未包含的插件,请直接将其添加到plugin
选项中。
exclude
Array<string|RegExp>
,默认 []
。
一个老是排除/移除的插件数组。
配置选项与 include
相同。
若是您不想使用 generators 而且不想包含 regeneratorRuntime
(使用 useBuiltIns
时),或者使用了其余插件(如 fast-async)而不是 Babel's async-to-gen,则此选项能够将 @babel/plugin-transform-regenerator
等转换禁用。
useBuiltIns
"usage" | "entry" | false
, 默认是 false
。
此选项将 core-js 模块直接引用为裸导入。所以,core-js 将相对于文件自己进行解析,而且是可被访问的。若是没有 core-js 依赖项或者有多个版本,您可能须要将 core-js@2 指定为应用程序中的顶级依赖项。
这个选项配置了 @babel/preset-env
如何处理 polyfills。
useBuiltIns: 'entry'
注意:只须要在你整个 app 中使用
require("@babel/polyfill");
一次。屡次对@babel/polyfill
的导入会致使全局冲突和其余很难跟踪的错误。咱们推荐建立一个单独的文件处理require
语句。
这个选项会启用一个新的插件,将 import "@babel/polyfill"
或者 require("@babel/polyfill")
替换为 @babel/polyfill
下的各个基于不一样环境的单独项导入。
npm install @babel/polyfill --save
复制代码
输入
import "@babel/polyfill"; 复制代码
输出(不一样的配置环境下有所区别)
import "core-js/modules/es7.string.pad-start"; import "core-js/modules/es7.string.pad-end"; 复制代码
也能够直接导入 core-js
(import "core-js";
or require('core-js');
)
useBuiltIns: 'usage'
(实验性)
在每一个文件中使用 polyfill 时,为 polyfill 添加特定导入。咱们利用 bundler 只加载一次相同的 polyfill。
输入
a.js
var a = new Promise(); 复制代码
b.js
var b = new Map(); 复制代码
输出(若是当前配置环境不支持此特性)
import "core-js/modules/es6.promise"; var a = new Promise(); 复制代码
import "core-js/modules/es6.map"; var b = new Map(); 复制代码
输出(若是当前配置环境支持此特性)
var a = new Promise(); 复制代码
var b = new Map(); 复制代码
useBuiltIns: false
既不会在每一个文件中自动添加 polyfill,也不会将 "@babel/polyfill" 导入为单个 polyfill。
简单总结
'usage'
和 'entry'
的区别:
'usage'
无需在头部引入 import '@babel/polyfill'
,它会自动根据当前的代码引入对应特性,而且只引用代码中用到的特性(browserslist 配置 + 代码用到)'entry'
须要在头部引入 '@babel/polyfill'
,而且是根据配置环境引入对应的特性。代码中没有用到,但环境中会缺失,也会引入。(只根据 browserslist 配置)
usage
风险项:因为咱们一般会使用不少 npm 的 dependencies 包来进行业务开发,babel 默认是不会检测 依赖包的代码的。 也就是说,若是某个 依赖包使用了Array.from
, 可是本身的业务代码没有使用到该API,构建出来的 polyfill 也不会有Array.from
, 如此一来,可能会在某些使用低版本浏览器的用户出现 BUG。 因此避免这种状况发生,通常开源的第三方库发布上线的时候都是转换成 ES5 的。
corejs
corejs
配置项是决定当前 Babel 使用的版本,有 2
和 3
选项。
升级文档中已经说明了,最新版的 Babel7 @babel/polyfill
移除了 polyfill proposals,因此 @babel/polyfill
仅仅是 core-js v2 的别名。
因此这里就须要注意的一点,若是使用 corejs: 2
+ useBuiltIns: 'entry'
的话,就会报警告:
`@babel/polyfill` is deprecated. Please, use required parts of `core-js` and `regenerator-runtime/runtime` separately
复制代码
这里须要使用的是 corejs: 3
+ useBuiltIns: 'entry'
,才不会出错。
forceAllTransforms
boolean
, 默认 false
。
因为有了 Babel7 Javascipt config file 的支持,你能够根据是否设置了 production
来控制转换。
module.exports = function(api) { return { presets: [ [ "@babel/preset-env", { targets: { chrome: 59, edge: 13, firefox: 50, }, // for uglifyjs... forceAllTransforms: api.env("production"), }, ], ], }; }; 复制代码
注意:
targets.uglify
已被废弃,而且在下一个版本中被移除。
默认状况下,此预设将运行目标环境所需的全部变换。若是你要强制运行全部转换,则启用此选项能够在须要用到 UglifyJS 或仅支持 ES5 语法的某些场景下会颇有用。
configPath
string
, 默认是 process.cwd()
决定配置 browserslist 搜索的起点,一直往上到系统根目录,直到找到。
ignoreBrowserslistConfig
boolean
, 默认是 false
切换是否使用 browserslist 配置源,包括搜索任何 browserslist 文件或引用package.json 中的 browserslist
键。这对于那些不走 Babel 编译,但使用 browserslist 配置的项目很是有用。
shippedProposals
boolean
, 默认是 false
切换启用对浏览器中提供的内置特性的支持。若是你的目标环境对某一个特性提案(proposal)具备原生支持,则会启用与其匹配的解析器语法插件,而不是执行任何转换。请注意,这不会启用与 @babel/preset-stage-3
相同的转换,由于这些提案可能在正式落地浏览器以前会有变动。
目前支持如下内容:
内置:
特性:
@babel/preset-stage-x
(废弃)在 babel7 中,官方已经宣布废弃 babel stage preset 包,大概是考虑到普遍使用的 stage-x 不适合社区的发展,具体缘由见官方博客
在新版的 babel 配置须要根据本身的须要下载对应的 proposal 插件,由于 stage-x 自己也是这些插件的集合,但不包含在 env 包中,好比安装:@babel/plugin-proposal-function-bind
,使用这些还没正式进标准但社区已经广为使用的语言特性。
具体使用的话,之前的各个 stage 等同于下面的各个插件的集合:
{ "plugins": [ // Stage 0 "@babel/plugin-proposal-function-bind", // Stage 1 "@babel/plugin-proposal-export-default-from", "@babel/plugin-proposal-logical-assignment-operators", ["@babel/plugin-proposal-optional-chaining", { "loose": false }], ["@babel/plugin-proposal-pipeline-operator", { "proposal": "minimal" }], ["@babel/plugin-proposal-nullish-coalescing-operator", { "loose": false }], "@babel/plugin-proposal-do-expressions", // Stage 2 ["@babel/plugin-proposal-decorators", { "legacy": true }], "@babel/plugin-proposal-function-sent", "@babel/plugin-proposal-export-namespace-from", "@babel/plugin-proposal-numeric-separator", "@babel/plugin-proposal-throw-expressions", // Stage 3 "@babel/plugin-syntax-dynamic-import", "@babel/plugin-syntax-import-meta", ["@babel/plugin-proposal-class-properties", { "loose": false }], "@babel/plugin-proposal-json-strings" ] } 复制代码
@babel/polyfill
官网信息:从 Babel 7.4.0 开始,这个包已经被废弃了,以支持直接导入
core-js/stable
(polyfill ECMAScript 特性)和regenerator-runtime/runtime
(须要使用转换后的 generator 函数)
babel-polyfill 的存在乎义是给浏览器“打补丁”,好比浏览器没有 Object.assign
这个特性,它会针对这个环境建立这个特性。Babel 自身是只转换语法,不添加丢失的特性,polyfill 的存在就是弥补浏览器这部分缺失的特性(好比某些 ie)。
babel-polyfill 等同于 regenerator runtime
+ core-js
。
最新版的具体用法,见 @babel/preset-env
的 useBuiltIns
特性。
通过个人简单实验,其实能够不用专门安装这个包,并且新的 corejs v3 和 corejs v2 还不太同样(使人困惑)。使用 useBuiltIns` 就好。
引入 babel-polyfill 也会有必定反作用,好比:
在应用开发中,上述行为问题不大,基本可控。但若是在库、工具的开发中引入 babel-polyfill,则会带来潜在的问题。
举个例子,在项目中定义了跟规范不一致的Array.from()函数,同时引入了一个库(依赖 babel-polyfill),此时,这个库可能覆盖了自定义的Array.from()函数,致使出错。
这就是 babel-runtime 存在的缘由。它将开发者依赖的全局内置对象等,抽取成单独的模块,并经过模块导入的方式引入,避免了对全局做用域的修改(污染)。
所以,若是是开发库、工具,能够考虑使用 babel-runtime。
@babel/runtime
@babel/runtime
是一个包含 Babel modular runtime helpers 和 一系列 regenerator-runtime 的库。
babel-runtime 通常用于两种场景:
与 babel-polyfill 的区别在于:
使用 babel-runtime 通常会搭配 babel-plugin-transform-runtime 使用。babel-plugin-transform-runtime 用于构建过程的代码转换,而 babel-runtime 是实际导入项目代码的功能模块。
@babel/plugin-transform-runtime
babel 在每一个须要的文件的顶部都会插入一些 helpers 内联代码,这可能会致使多个文件都会有重复的 helpers 代码。@babel/plugin-transform-runtime
的 helpers 选项就能够把这些模块抽离出来。
@babel/plugin-transform-runtime
主要作了三件事情:core-js aliasing、helper aliasing、egenerator aliasing。
core-js aliasing:自动导入babel-runtime/core-js,并将全局静态方法、全局内置对象 映射到对应的模块。
helper aliasing:将内联的工具函数移除,改为经过babel-runtime/helpers模块进行导入,好比_classCallCheck工具函数。
regenerator aliasing:若是你使用了 async/generator 函数,则自动导入 babel-runtime/regenerator模块。
module.exports = { "plugins": [ [ "@babel/plugin-transform-runtime", { "corejs": false, // boolean 或者 number, 默认 false,指定是否须要 runtime 的 corejs aliasing,若是使用 env 的 useBuiltIns + polyfill,使用 false。 "helpers": true, // boolean, 默认 true,指定是否内联 babel 的 helper 代码 (好比 classCallCheck, extends) "regenerator": false, // 经过 preset-env 已经使用了全局的 regeneratorRuntime, 再也不须要 transform-runtime 提供的 不污染全局的 regeneratorRuntime "useESModules": true, // boolean, 默认 false,使用 es modules helpers, 减小 commonJS 语法代码 "absoluteRuntime": false // boolean, 默认 false,是否目录引用 runtime 包(有些项目会引用当前项目以外的代码,编译时会找不到 runtime 包) } ] ] } 复制代码
添加新配置前编译出来的代码
import "core-js/modules/es6.promise"; import "regenerator-runtime/runtime"; function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } 复制代码
添加新配置后编译出来的代码
import "core-js/modules/es6.promise"; import "regenerator-runtime/runtime"; import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator"; 复制代码
babel-register
babel-register
则提供了动态编译。换句话说,咱们的源代码可以真正运行在生产环境下,不须要 babel 编译这一环节。
咱们先在项目下安装 babel-register
:
$ npm install --save-dev @babel/register
复制代码
而后在入口文件中 require
:
require('@babel/register') require('./app') 复制代码
在入口文件头部引入 @babel/register
后,咱们的 app 文件中便可使用任意 es2015 的特性。
固然,坏处是动态编译,致使程序在速度、性能上有所损耗。因此这一项基本不用在正式的生产环境中使用。
babel-node
上面所说,babel-register
提供动态编译,可以让咱们的源代码真正运行在生产环境下 - 但其实否则,咱们仍须要作部分调整,好比新增一个入口文件,并在该文件中 require('@babel/register')
。而 babel-node 能真正作到一行源代码都不须要调整:
$ npm install --save-dev @babel/core @babel/node
$ npx babel-node app.js
复制代码
只是,请不要在生产环境中使用 babel-node,由于它是动态编译源代码,应用启动速度很是慢。
依赖:
@babel/core
(核心包)@babel/preset-env
(预设)@babel/polyfill
(v7.4.0彷佛被废弃,能够不用安装)core-js
(最新版本v3,在配置版本corejs:3的状况下,这个包是用于替代 polyfill 的)@babel/runtime
(开发业务代码基本只用到helper配置,开发技术库能够深刻使用)@babel/plugin-transform-runtime
(合并重复的 helper 函数)@babel/plugin-proposal-function-bind
(没有 stage-x 后,须要安装单独的插件,支持对应的 proposal 特性)注意:这里
@babel/polyfill
可装可不装,不装彷佛也不影响没有影响,但不肯定正式允运行的时候会不会报错。看了下源码,其实很简单,就是引用到 core-js v2 的特性。官方文档介绍已经被废弃了。
const presets = [ [ "@babel/env", { targets: { edge: "17", firefox: "60", chrome: "67", safari: "11.1", ie: '8' }, useBuiltIns: 'usage', // Babel7 须要指定引入corejs的版本,最好使用3 corejs: 3, modules: 'amd', // 须要转换成什么样的模块系统 }, ], ]; const plugins = [ // 帮助减小 helper 函数 [ "@babel/plugin-transform-runtime", { "corejs": false, // 默认值,能够不写 "helpers": true, // 默认,能够不写 "regenerator": false, // 经过 preset-env 已经使用了全局的 regeneratorRuntime, 再也不须要 transform-runtime 提供的 不污染全局的 regeneratorRuntime "useESModules": true, // 使用 es modules helpers, 减小 commonJS 语法代码 } ], // 因为没有了 stage-x,须要单独导入须要的插件 [ '@babel/plugin-proposal-function-bind' ] ] module.exports = { presets, plugins }; 复制代码