文章总结的时间是2017/11/20
本文是为了梳理Babel配置及使用而整理,因为看过使用Babel配置项目和文章,存在项目插件使用混乱、文章各种照搬、插件使用听风是雨、插件升级文章内容不再适用的问题。这里就目前最新使用的配置组合进行整理,涉及的插件包括以下三个:
@babel/preset-env(^7.0.0-beta.32) @babel/preset-stage-x(7.0.0-beta.32), x-0,1,2,3 @babel/polyfill(^7.0.0-beta.32) @babel/preset-env
替换之前所有
babel-presets-es20xx
插件A Babel preset that compiles ES2015+ down to ES5 by automatically determining the Babel plugins and polyfills you need based on your targeted browser or runtime environments.
也就是说,这是一个能根据运行环境为代码做相应的编译,
@babel/preset-env
的推出是为了解解决个性化输出目标代码的问题,通过browserslist
语法解析需要支持的目标环境,根据环境将源代码转义到目标代码,可以实现代码精准适配。此外,
@babel/preset-env
不包含state-x
一些列插件 ,只支持最新推出版本的JavaScript语法(state-4),关于state-x
后面会介绍。更进一步说明,请参考 Dr. Axel Rauschmayer 的介绍。这个插件对特殊平台的开发有很大帮助,比如:Electron、大屏、移动端(只考虑webkit)等。
替换
@babel/plugin-transform-runtime
的使用
@babel/plugin-transform-runtime
插件是为了解决:多个文件重复引用相同helpers(帮助函数)-> 提取运行时 新API方法全局污染 -> 局部引入 这个插件推荐在编写library/module时使用。当然,以上问题可通过设置
useBuiltIns
搞定。支持实例方法按需引入
传统方式是手动从
core-js
引入需要的ES6+特性,require('core-js/fn/set'); require('core-js/fn/array/from'); require('core-js/fn/array/find-index');
或者一股脑全部引入:
import '@babel/polyfill' // or require('@babel/polyfill')
同样,以上问题可通过设置
useBuiltIns
搞定。默认情况下,
@babel/preset-env
的效果和@babel/preset-latest
一样,虽然上面的说明有提到polyfills,但是也需要在配置中设置useBuiltIns
才会生效。targets
设置支持环境,支持的
"presets": [ ["@babel/env", { "targets": { "node": "current", "chrome": 52, "browsers": ["last 2 versions", "safari 7"]key
包括:chrome, opera, edge, firefox, safari, ie, ios, android, node, electron。其中,
browserslist
可在package.json
中配置,这个和设置CSS的Autoprefixer一致,配置优先级如下:targets.browsers > package.json/browserslist
此外,
browserslist
的配置满足最大匹配原则,比如需要同时支持在 IE 8 和 Chrome 55 下运行,则preset-env
提供所有 IE 8 下需要的插件,即使 Chrome 55 可能不需要。modules
选项用于模块转化规则设置,可选配置包括:"amd" | "umd" | "systemjs" | "commonjs" | false, 默认使用 "commonjs"。即,将代码中的ES6的
import
转为require
。如果你当前的webpack构建环境是2.x/3.x,推荐将
modules
设置为false
,即交由 Webpack 来处理模块化,通过其 TreeShaking 特性将有效减少打包出来的 JS 文件大小。这部分参考这里的回答:ECMAScript 6 的模块相比 CommonJS 的require (...)有什么优点?useBuiltIns
A way to apply @babel/preset-env for polyfills (via @babel/polyfill).
可选值包括:"usage" | "entry" | false, 默认为 false,表示不对 polyfills 处理,这个配置是引入 polyfills 的关键。
"useBuiltIns":"usage"
在文件需要的位置单独按需引入,可以保证在每个bundler中只引入一份。例如:
var a = new Promise();
var b = new Map();
Out (if environment doesn't support it)
import "core-js/modules/es6.promise"; var a = new Promise();
import "core-js/modules/es6.map"; var b = new Map();
Out (if environment supports it)
var a = new Promise(); var b = new Map();
!!注意!!
当前模式类似于
@babel/plugin-transform-runtime
,polyfill局部使用,制造一个沙盒环境,不造成全局污染,但是如上配置后,@babel/preset-env
能按需引入新实例方法,例如:"foobar".includes("foo")
而
@babel/plugin-transform-runtime
不行,需要自行从core-js
中按需引入。我测试的情况和下面这篇文章所说完全不一致。原文:https://zhuanlan.zhihu.com/p/29506685
当 useBuiltIns 设置为 usage 时,Babel 会在你使用到 ES2015+ 新特性时,自动添加 babel-polyfill 的引用,并且是 partial 级别的引用。
请注意: usage 的行为类似 babel-transform-runtime,不会造成全局污染,因此也会不会对类似 Array.prototype.includes() 进行 polyfill。
"useBuiltIns":"entry"
在项目入口引入一次(多次引入会报错)
import "@babel/polyfill" // or require("@babel/polyfill")
插件
@babel/preset-env
会将把@babel/polyfill
根据实际需求打散,只留下必须的,例如:import "@babel/polyfill";
Out (different based on environment)
import "core-js/modules/es6.promise"; import "core-js/modules/es7.string.pad-start"; import "core-js/modules/es7.string.pad-end"; import "core-js/modules/es7.array.includes";
除了新API,也可以是实例方法,不过最终包体积比使用
"useBuiltIns":"usage"
大30kb左右(因项目而异)。"useBuiltIns": false
不在代码中使用polyfills,表现形式和
@babel/preset-latest
一样,当使用ES6+语法及API时,在不支持的环境下会报错。@babel/preset-stage-x
这里需要说明下ES特性支持的提案,不同阶段的提案支持的内容不同,其中
stage-4
阶段提案中的特性将会在未来发布。关于各个Stage的说明参考这里。上面介绍的
@babel/preset-env
或者@babel/preset-latest
就是下面提到的stage-4
。The TC39 categorizes proposals into the following stages:
Stage 0 - Strawman: just an idea, possible Babel plugin. Stage 1 - Proposal: this is worth working on. Stage 2 - Draft: initial spec. Stage 3 - Candidate: complete spec and initial browser implementations. Stage 4 - Finished: will be added to the next yearly release. Stage的包含顺序是:左边包含右边全部特性,即stage-0包含右边 1 / 2 / 3 的所有插件。
stage-0 > ~1 > ~2 > ~3 > ~4:
1. 这个和
@babel/preset-env
的区别
@babel/preset-env
会根据预设的浏览器兼容列表从stage-4
选取必须的plugin,也就是说,不引入别的stage-x
,@babel/preset-env
将只支持到stage-4
。1. 如果是React用户,建议配到
@babel/preset-stage-0
其中的两个插件对于写JSX很有帮助。
transform-do-expressions:if/else三目运算展开 transform-function-bind:this绑定 2. 通常使用建议配到
@babel/preset-stage-2
插件包括:
syntax-dynamic-import: 动态import transform-class-properties:用于 class 的属性转化 transform-object-rest-spread:用来处理 rest spread transform-async-generator-functions:用来处理 async 和 await @babel/polyfill
这个插件是对core-js和regenerator-runtime的再次封装,在
@babel/preset-env
中的useBuiltIns: entry
用到,代码不多。if (global._babelPolyfill) { throw new Error("only one instance of @babel/polyfill is allowed"); global._babelPolyfill = true; import "core-js/shim"; import "regenerator-runtime/runtime";
core-js/shim
shim only: Only includes the standard methods.
只包含了纳入标准的API及实例化方法,例如下列常见的。注意,这里没有generator/async,如果需要,那就安装插件
require('./modules/es6.string.trim'); require('./modules/es6.string.includes'); require('./modules/es7.array.includes'); require('./modules/es6.promise');@babel/preset-stage-3
(或者0 / 1 / 2)regenerator-runtime/runtime
Standalone runtime for Regenerator-compiled generator and async functions.
主要是给generator/async做支持的插件。
这里需要理解下三个概念:
最新ES 语法:比如,箭头函数 最新ES API:,比如,Promise 最新ES 实例方法:比如,String.protorype.includes
@babel/preset-env
默认支持语法转化,需要开启useBuiltIns
配置才能转化API和实例方法。此外,不管是写项目还是写Library/Module,使用
@babel/preset-env
并正确配置就行。多看英文原稿说明,中文总结看看就好,别太当真。@babel/preset-env @babel/preset-stage-0 @babel/preset-stage-1 @babel/preset-stage-2 @babel/preset-stage-3