本文發佈於 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 }; 複製代碼