文章總結(jié)的時(shí)間是2017/11/20
本文是為了梳理Babel配置及使用而整理,因?yàn)榭催^(guò)使用Babel配置項(xiàng)目和文章,存在項(xiàng)目插件使用混亂、文章各種照搬、插件使用聽(tīng)風(fēng)是雨、插件升級(jí)文章內(nèi)容不再適用的問(wèn)題。這里就目前最新使用的配置組合進(jìn)行整理,涉及的插件包括以下三個(gè):
- @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.
也就是說(shuō),這是一個(gè)能根據(jù)運(yùn)行環(huán)境為代碼做相應(yīng)的編譯,@babel/preset-env的推出是為了解解決個(gè)性化輸出目標(biāo)代碼的問(wèn)題,通過(guò)browserslist語(yǔ)法解析需要支持的目標(biāo)環(huán)境,根據(jù)環(huán)境將源代碼轉(zhuǎn)義到目標(biāo)代碼,可以實(shí)現(xiàn)代碼精準(zhǔn)適配。
此外,@babel/preset-env不包含state-x一些列插件,只支持最新推出版本的JavaScript語(yǔ)法(state-4),關(guān)于state-x后面會(huì)介紹。
更進(jìn)一步說(shuō)明,請(qǐng)參考Dr. Axel Rauschmayer的介紹。這個(gè)插件對(duì)特殊平臺(tái)的開(kāi)發(fā)有很大幫助,比如:Electron、大屏、移動(dòng)端(只考慮webkit)等。
替換@babel/plugin-transform-runtime的使用
@babel/plugin-transform-runtime插件是為了解決:
- 多個(gè)文件重復(fù)引用相同helpers(幫助函數(shù))-> 提取運(yùn)行時(shí)
- 新API方法全局污染 -> 局部引入
這個(gè)插件推薦在編寫(xiě)library/module時(shí)使用。當(dāng)然,以上問(wèn)題可通過(guò)設(shè)置useBuiltIns搞定。
支持實(shí)例方法按需引入
傳統(tǒng)方式是手動(dòng)從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')
同樣,以上問(wèn)題可通過(guò)設(shè)置useBuiltIns搞定。
使用說(shuō)明
默認(rèn)情況下,@babel/preset-env的效果和@babel/preset-latest一樣,雖然上面的說(shuō)明有提到polyfills,但是也需要在配置中設(shè)置useBuiltIns才會(huì)生效。
主要配置
targets
設(shè)置支持環(huán)境,支持的key包括:chrome, opera, edge, firefox, safari, ie, ios, android, node, electron。
例如:
{
"presets": [
["@babel/env", {
"targets": {
"node": "current",
"chrome": 52,
"browsers": ["last 2 versions", "safari 7"]
}
}]
]
}
其中,browserslist可在package.json中配置,這個(gè)和設(shè)置CSS的Autoprefixer一致,配置優(yōu)先級(jí)如下:
targets.browsers > package.json/browserslist
此外,browserslist的配置滿(mǎn)足最大匹配原則,比如需要同時(shí)支持在 IE 8 和 Chrome 55 下運(yùn)行,則preset-env提供所有 IE 8 下需要的插件,即使 Chrome 55 可能不需要。
modules
選項(xiàng)用于模塊轉(zhuǎn)化規(guī)則設(shè)置,可選配置包括:"amd" | "umd" | "systemjs" | "commonjs" | false, 默認(rèn)使用 "commonjs"。即,將代碼中的ES6的import轉(zhuǎn)為require。
如果你當(dāng)前的webpack構(gòu)建環(huán)境是2.x/3.x,推薦將modules設(shè)置為false,即交由 Webpack 來(lái)處理模塊化,通過(guò)其 TreeShaking 特性將有效減少打包出來(lái)的 JS 文件大小。這部分參考這里的回答:ECMAScript 6 的模塊相比 CommonJS 的require (...)有什么優(yōu)點(diǎn)?
useBuiltIns
A way to apply @babel/preset-env for polyfills (via @babel/polyfill).
可選值包括:"usage" | "entry" | false, 默認(rèn)為 false,表示不對(duì) polyfills 處理,這個(gè)配置是引入 polyfills 的關(guān)鍵。
"useBuiltIns":"usage"
在文件需要的位置單獨(dú)按需引入,可以保證在每個(gè)bundler中只引入一份。例如:
In
a.js
var a = new Promise();
b.js
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();
??!注意?。?/strong>
當(dāng)前模式類(lèi)似于@babel/plugin-transform-runtime,polyfill局部使用,制造一個(gè)沙盒環(huán)境,不造成全局污染,但是如上配置后,@babel/preset-env能按需引入新實(shí)例方法,例如:
"foobar".includes("foo")
而@babel/plugin-transform-runtime不行,需要自行從core-js中按需引入。我測(cè)試的情況和下面這篇文章所說(shuō)完全不一致。
原文:https://zhuanlan.zhihu.com/p/29506685
當(dāng) useBuiltIns 設(shè)置為 usage 時(shí),Babel 會(huì)在你使用到 ES2015+ 新特性時(shí),自動(dòng)添加 babel-polyfill 的引用,并且是 partial 級(jí)別的引用。
請(qǐng)注意: usage 的行為類(lèi)似 babel-transform-runtime,不會(huì)造成全局污染,因此也會(huì)不會(huì)對(duì)類(lèi)似 Array.prototype.includes() 進(jìn)行 polyfill。
"useBuiltIns":"entry"
在項(xiàng)目入口引入一次(多次引入會(huì)報(bào)錯(cuò))
import "@babel/polyfill"
// or
require("@babel/polyfill")
插件@babel/preset-env會(huì)將把@babel/polyfill根據(jù)實(shí)際需求打散,只留下必須的,例如:
In
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,也可以是實(shí)例方法,不過(guò)最終包體積比使用
"useBuiltIns":"usage"大30kb左右(因項(xiàng)目而異)。
"useBuiltIns": false
不在代碼中使用polyfills,表現(xiàn)形式和@babel/preset-latest一樣,當(dāng)使用ES6+語(yǔ)法及API時(shí),在不支持的環(huán)境下會(huì)報(bào)錯(cuò)。
@babel/preset-stage-x
這里需要說(shuō)明下ES特性支持的提案,不同階段的提案支持的內(nèi)容不同,其中stage-4階段提案中的特性將會(huì)在未來(lái)發(fā)布。
關(guān)于各個(gè)Stage的說(shuō)明參考這里。上面介紹的@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:
疑問(wèn)
1. 這個(gè)和@babel/preset-env的區(qū)別
@babel/preset-env會(huì)根據(jù)預(yù)設(shè)的瀏覽器兼容列表從stage-4選取必須的plugin,也就是說(shuō),不引入別的stage-x,@babel/preset-env將只支持到stage-4。
建議
1. 如果是React用戶(hù),建議配到@babel/preset-stage-0
其中的兩個(gè)插件對(duì)于寫(xiě)JSX很有幫助。
- transform-do-expressions:if/else三目運(yùn)算展開(kāi)
- transform-function-bind:this綁定
2. 通常使用建議配到@babel/preset-stage-2
插件包括:
- syntax-dynamic-import: 動(dòng)態(tài)import
- transform-class-properties:用于 class 的屬性轉(zhuǎn)化
- transform-object-rest-spread:用來(lái)處理 rest spread
- transform-async-generator-functions:用來(lái)處理 async 和 await
@babel/polyfill
這個(gè)插件是對(duì)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.
只包含了納入標(biāo)準(zhǔn)的API及實(shí)例化方法,例如下列常見(jiàn)的。注意,這里沒(méi)有g(shù)enerator/async,如果需要,那就安裝插件@babel/preset-stage-3(或者0 / 1 / 2)
...
require('./modules/es6.string.trim');
require('./modules/es6.string.includes');
require('./modules/es7.array.includes');
require('./modules/es6.promise');
...
regenerator-runtime/runtime
Standalone runtime for Regenerator-compiled generator and async functions.
主要是給generator/async做支持的插件。
總結(jié)
這里需要理解下三個(gè)概念:
- 最新ES 語(yǔ)法:比如,箭頭函數(shù)
- 最新ES API:,比如,Promise
- 最新ES 實(shí)例方法:比如,String.protorype.includes
@babel/preset-env默認(rèn)支持語(yǔ)法轉(zhuǎn)化,需要開(kāi)啟useBuiltIns配置才能轉(zhuǎn)化API和實(shí)例方法。
此外,不管是寫(xiě)項(xiàng)目還是寫(xiě)Library/Module,使用@babel/preset-env并正確配置就行。多看英文原稿說(shuō)明,中文總結(jié)看看就好,別太當(dāng)真。