不容錯(cuò)過(guò)的 Babel7 知識(shí)
對(duì) Babel 的配置項(xiàng)的作用不那么了解,是否會(huì)影響日常開(kāi)發(fā)呢?老實(shí)說(shuō),大多情況下沒(méi)有什么影響。
不過(guò)呢,還是想更進(jìn)一步了解下,于是最近認(rèn)真閱讀了 Babel 的文檔,外加不斷編譯驗(yàn)證,輸出了本篇文章,為了更好的閱讀體驗(yàn),修修改改,最終算是以我個(gè)人比較喜歡的方式推進(jìn)了每個(gè)知識(shí)點(diǎn)(每一個(gè)配置的引入都是有原因的),希望能夠幫助你對(duì) Babel 的各種配置有一個(gè)更清晰的認(rèn)識(shí)。
Babel 是一個(gè) JS 編譯器
Babel 是一個(gè)工具鏈,主要用于將 ECMAScript 2015+ 版本的代碼轉(zhuǎn)換為向后兼容的 JavaScript 語(yǔ)法,以便能夠運(yùn)行在當(dāng)前和舊版本的瀏覽器或其他環(huán)境中。
我們先看看 Babel 能夠做什么:
語(yǔ)法轉(zhuǎn)換
通過(guò)
Polyfill方式在目標(biāo)環(huán)境中添加缺失的特性(@babel/polyfill模塊)源碼轉(zhuǎn)換(codemods)
本篇文章的目的是搞明白 Babel 的使用和配置,搞清楚 @babel/runtime,@babel/polyfill,@babel/plugin-transform-runtime 這些作用是什么,插件和預(yù)設(shè)都是用來(lái)干什么的,我們?yōu)槭裁葱枰渲盟鼈儯皇侵v如何進(jìn)行 AST 轉(zhuǎn)換,如果你對(duì) AST 轉(zhuǎn)換非常感興趣,歡迎閱讀我們的 RN轉(zhuǎn)小程序引擎 Alita 的源碼,其中應(yīng)用了大量的 AST 轉(zhuǎn)換。
更多文章可戳(如Star,謝謝你):https://github.com/YvetteLau/Blog
為了更清晰的了解每一步,首先創(chuàng)建一個(gè)新項(xiàng)目,例如 babelTemp(你愛(ài)取啥名取啥名),使用 npm init -y 進(jìn)行初始化,創(chuàng)建 src/index.js,文件內(nèi)容如下(你也可以隨便寫(xiě)點(diǎn)什么):
const fn = () => {
console.log('a');
};
OK,創(chuàng)建好的項(xiàng)目先放在一邊,先了解下理論知識(shí):
核心庫(kù) @babel/core
Babel 的核心功能包含在 @babel/core 模塊中。看到 core 這個(gè)詞了吧,意味著核心,沒(méi)有它,在 babel 的世界里注定寸步難行。不安裝 @babel/core,無(wú)法使用 babel 進(jìn)行編譯。
CLI命令行工具 @babel/cli
babel 提供的命令行工具,主要是提供 babel 這個(gè)命令,適合安裝在項(xiàng)目里。
@babel/node 提供了 babel-node 命令,但是 @babel/node 更適合全局安裝,不適合安裝在項(xiàng)目里。
npm install --save-dev @babel/core @babel/cli
現(xiàn)在你就可以在項(xiàng)目中使用 babel 進(jìn)行編譯啦(如果不安裝 @babel/core,會(huì)報(bào)錯(cuò)噢)
將命令配置在 package.json 文件的 scripts 字段中:
//...
"scripts": {
"compiler": "babel src --out-dir lib --watch"
}
使用 npm run compiler 來(lái)執(zhí)行編譯,現(xiàn)在我們沒(méi)有配置任何插件,編譯前后的代碼是完全一樣的。
因?yàn)?Babel 雖然開(kāi)箱即用,但是什么動(dòng)作也不做,如果想要 Babel 做一些實(shí)際的工作,就需要為其添加插件(plugin)。
插件
Babel 構(gòu)建在插件之上,使用現(xiàn)有的或者自己編寫(xiě)的插件可以組成一個(gè)轉(zhuǎn)換通道,Babel 的插件分為兩種: 語(yǔ)法插件和轉(zhuǎn)換插件。
語(yǔ)法插件
這些插件只允許 Babel 解析(parse) 特定類型的語(yǔ)法(不是轉(zhuǎn)換),可以在 AST 轉(zhuǎn)換時(shí)使用,以支持解析新語(yǔ)法,例如:
import * as babel from "@babel/core";
const code = babel.transformFromAstSync(ast, {
//支持可選鏈
plugins: ["@babel/plugin-proposal-optional-chaining"],
babelrc: false
}).code;
轉(zhuǎn)換插件
轉(zhuǎn)換插件會(huì)啟用相應(yīng)的語(yǔ)法插件(因此不需要同時(shí)指定這兩種插件),這點(diǎn)很容易理解,如果不啟用相應(yīng)的語(yǔ)法插件,意味著無(wú)法解析,連解析都不能解析,又何談轉(zhuǎn)換呢?
插件的使用
如果插件發(fā)布在 npm 上,可以直接填寫(xiě)插件的名稱, Babel 會(huì)自動(dòng)檢查它是否已經(jīng)被安裝在 node_modules 目錄下,在項(xiàng)目目錄下新建 .babelrc 文件 (下文會(huì)具體介紹配置文件),配置如下:
//.babelrc
{
"plugins": ["@babel/plugin-transform-arrow-functions"]
}
也可以指定插件的相對(duì)/絕對(duì)路徑
{
"plugins": ["./node_modules/@babel/plugin-transform-arrow-functions"]
}
執(zhí)行 npm run compiler,可以看到箭頭函數(shù)已經(jīng)被編譯OK, lib/index.js 內(nèi)容如下:
const fn = function () {
console.log('a');
};
現(xiàn)在,我們僅支持轉(zhuǎn)換箭頭函數(shù),如果想將其它的新的JS特性轉(zhuǎn)換成低版本,需要使用其它對(duì)應(yīng)的 plugin 。如果我們一個(gè)個(gè)配置的話,會(huì)非常繁瑣,因?yàn)槟憧赡苄枰渲脦资畟€(gè)插件,這顯然非常不便,那么有沒(méi)有什么辦法可以簡(jiǎn)化這個(gè)配置呢?
有!預(yù)設(shè)!(感謝強(qiáng)大的 Babel)

預(yù)設(shè)
通過(guò)使用或創(chuàng)建一個(gè) preset 即可輕松使用一組插件。
官方 Preset
@babel/preset-env
@babel/preset-flow
@babel/preset-react
@babel/preset-typescript
注: 從 Babel v7 開(kāi)始,所以針對(duì)標(biāo)準(zhǔn)提案階段的功能所編寫(xiě)的預(yù)設(shè)(stage preset)都已被棄用,官方已經(jīng)移除了 @babel/preset-stage-x。
@babel/preset-env
@babel/preset-env 主要作用是對(duì)我們所使用的并且目標(biāo)瀏覽器中缺失的功能進(jìn)行代碼轉(zhuǎn)換和加載 polyfill,在不進(jìn)行任何配置的情況下,@babel/preset-env 所包含的插件將支持所有最新的JS特性(ES2015,ES2016等,不包含 stage 階段),將其轉(zhuǎn)換成ES5代碼。例如,如果你的代碼中使用了可選鏈(目前,仍在 stage 階段),那么只配置 @babel/preset-env,轉(zhuǎn)換時(shí)會(huì)拋出錯(cuò)誤,需要另外安裝相應(yīng)的插件。
//.babelrc
{
"presets": ["@babel/preset-env"]
}
需要說(shuō)明的是,@babel/preset-env 會(huì)根據(jù)你配置的目標(biāo)環(huán)境,生成插件列表來(lái)編譯。對(duì)于基于瀏覽器或 Electron 的項(xiàng)目,官方推薦使用 .browserslistrc 文件來(lái)指定目標(biāo)環(huán)境。默認(rèn)情況下,如果你沒(méi)有在 Babel 配置文件中(如 .babelrc)設(shè)置 targets 或 ignoreBrowserslistConfig,@babel/preset-env 會(huì)使用 browserslist 配置源。
如果你不是要兼容所有的瀏覽器和環(huán)境,推薦你指定目標(biāo)環(huán)境,這樣你的編譯代碼能夠保持最小。
例如,僅包括瀏覽器市場(chǎng)份額超過(guò)0.25%的用戶所需的 polyfill 和代碼轉(zhuǎn)換(忽略沒(méi)有安全更新的瀏覽器,如 IE10 和 BlackBerry):
//.browserslistrc
> 0.25%
not dead
查看 browserslist 的更多配置
例如,你將 .browserslistrc 的內(nèi)容配置為:
last 2 Chrome versions
然后再執(zhí)行 npm run compiler,你會(huì)發(fā)現(xiàn)箭頭函數(shù)不會(huì)被編譯成ES5,因?yàn)?chrome 的最新2個(gè)版本都能夠支持箭頭函數(shù)。現(xiàn)在,我們將 .browserslistrc 仍然換成之前的配置。
就咱們目前的代碼來(lái)說(shuō),當(dāng)前的配置似乎已經(jīng)是OK的了。

我們修改下 src/index.js。
const isHas = [1,2,3].includes(2);
const p = new Promise((resolve, reject) => {
resolve(100);
});
編譯出來(lái)的結(jié)果為:
"use strict";
var isHas = [1, 2, 3].includes(2);
var p = new Promise(function (resolve, reject) {
resolve(100);
});
這個(gè)編譯出來(lái)的代碼在低版本瀏覽器中使用的話,顯然是有問(wèn)題的,因?yàn)榈桶姹緸g覽器中數(shù)組實(shí)例上沒(méi)有 includes 方法,也沒(méi)有 Promise 構(gòu)造函數(shù)。
這是為什么呢?因?yàn)檎Z(yǔ)法轉(zhuǎn)換只是將高版本的語(yǔ)法轉(zhuǎn)換成低版本的,但是新的內(nèi)置函數(shù)、實(shí)例方法無(wú)法轉(zhuǎn)換。這時(shí),就需要使用 polyfill 上傳了,顧名思義,polyfill的中文意思是墊片,所謂墊片就是墊平不同瀏覽器或者不同環(huán)境下的差異,讓新的內(nèi)置函數(shù)、實(shí)例方法等在低版本瀏覽器中也可以使用。

Polyfill
@babel/polyfill 模塊包括 core-js 和一個(gè)自定義的 regenerator runtime 模塊,可以模擬完整的 ES2015+ 環(huán)境(不包含第4階段前的提議)。
這意味著可以使用諸如 Promise 和 WeakMap 之類的新的內(nèi)置組件、 Array.from 或 Object.assign 之類的靜態(tài)方法、Array.prototype.includes 之類的實(shí)例方法以及生成器函數(shù)(前提是使用了 @babel/plugin-transform-regenerator 插件)。為了添加這些功能,polyfill 將添加到全局范圍和類似 String 這樣的內(nèi)置原型中(會(huì)對(duì)全局環(huán)境造成污染,后面我們會(huì)將不污染全局環(huán)境的方法)。
首先,安裝 @babel/polyfill 依賴:
npm install --save @babel/polyfill
注意:不使用 --save-dev,因?yàn)檫@是一個(gè)需要在源碼之前運(yùn)行的墊片。
我們需要將完整的 polyfill 在代碼之前加載,修改我們的 src/index.js:
import '@babel/polyfill';
const isHas = [1,2,3].includes(2);
const p = new Promise((resolve, reject) => {
resolve(100);
});
@babel/polyfill 需要在其它代碼之前引入,我們也可以在 webpack 中進(jìn)行配置。
例如:
entry: [
require.resolve('./polyfills'),
path.resolve('./index')
]
polyfills.js 文件內(nèi)容如下:
//當(dāng)然,還可能有一些其它的 polyfill,例如 stage 4之前的一些 polyfill
import '@babel/polyfill';
現(xiàn)在,我們的代碼不管在低版本還是高版本瀏覽器(或node環(huán)境)中都能正常運(yùn)行了。不過(guò),很多時(shí)候,我們未必需要完整的 @babel/polyfill,這會(huì)導(dǎo)致我們最終構(gòu)建出的包的體積增大,@babel/polyfill的包大小為89K (當(dāng)前 @babel/polyfill 版本為 7.7.0)。
我們更期望的是,如果我使用了某個(gè)新特性,再引入對(duì)應(yīng)的 polyfill,避免引入無(wú)用的代碼。
值得慶幸的是, Babel 已經(jīng)考慮到了這一點(diǎn)。

@babel/preset-env 提供了一個(gè) useBuiltIns 參數(shù),設(shè)置值為 usage 時(shí),就只會(huì)包含代碼需要的 polyfill 。有一點(diǎn)需要注意:配置此參數(shù)的值為 usage ,必須要同時(shí)設(shè)置 corejs (如果不設(shè)置,會(huì)給出警告,默認(rèn)使用的是"corejs": 2) ,注意: 這里仍然需要安裝 @babel/polyfill(當(dāng)前 @babel/polyfill 版本默認(rèn)會(huì)安裝 "corejs": 2):
首先說(shuō)一下使用 core-js@3 的原因,core-js@2 分支中已經(jīng)不會(huì)再添加新特性,新特性都會(huì)添加到 core-js@3。例如你使用了 Array.prototype.flat(),如果你使用的是 core-js@2,那么其不包含此新特性。為了可以使用更多的新特性,建議大家使用 core-js@3。
安裝依賴依賴:
npm install --save core-js@3
core-js (點(diǎn)擊了解更多) : JavaScript 的模塊化標(biāo)準(zhǔn)庫(kù),包含
Promise、Symbol、Iterator和許多其他的特性,它可以讓你僅加載必需的功能。
現(xiàn)在,修改 Babel 的配置文件如下:
//.babelrc
const presets = [
[
"@babel/env",
{
"useBuiltIns": "usage",
"corejs": 3
}
]
]
Babel 會(huì)檢查所有代碼,以便查找在目標(biāo)環(huán)境中缺失的功能,然后僅僅把需要的 polyfill 包含進(jìn)來(lái)。
例如,src/index.js 代碼不變:
const isHas = [1,2,3].includes(2);
const p = new Promise((resolve, reject) => {
resolve(100);
});
我們看看編譯出來(lái)的文件(lib/index):
"use strict";
require("core-js/modules/es.array.includes");
require("core-js/modules/es.object.to-string");
require("core-js/modules/es.promise");
var isHas = [1, 2, 3].includes(2);
var p = new Promise(function (resolve, reject) {
resolve(100);
});
同樣的代碼,我們用 webpack 構(gòu)建一下(production 模式),能看到最終的代碼大小僅為: 20KB。而如果我們引入整個(gè) @babel/polyfill 的話,構(gòu)建出的包大小為:89KB
前面曾提到,在 useBuiltIns 參數(shù)值為 usage 時(shí),仍然需要安裝 @babel/polyfill,雖然我們上面的代碼轉(zhuǎn)換中看起來(lái)并沒(méi)有使用到,但是,如果我們?cè)创a中使用到了 async/await,那么編譯出來(lái)的代碼需要 require("regenerator-runtime/runtime"),在 @babel/polyfill 的依賴中,當(dāng)然啦,你也可以只安裝 regenerator-runtime/runtime 取代安裝 @babel/polyfill。
到了這一步,已經(jīng)很棒棒了,是不是想跳起來(lái)轉(zhuǎn)個(gè)圈圈?

下面我要說(shuō)的內(nèi)容,也許你已經(jīng)知道,也許你還不知道,這都不重要,但是此刻起,你要知道了: Babel 會(huì)使用很小的輔助函數(shù)來(lái)實(shí)現(xiàn)類似 _createClass 等公共方法。默認(rèn)情況下,它將被添加(inject)到需要它的每個(gè)文件中。
假如,我們的 src/index.js 是這樣的:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
};
getX() {
return this.x;
}
}
let cp = new ColorPoint(25, 8);
編譯出來(lái)的 lib/index.js,如下所示:
"use strict";
require("core-js/modules/es.object.define-property");
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
var Point =
/*#__PURE__*/
function () {
function Point(x, y) {
_classCallCheck(this, Point);
this.x = x;
this.y = y;
}
_createClass(Point, [{
key: "getX",
value: function getX() {
return this.x;
}
}]);
return Point;
}();
var cp = new ColorPoint(25, 8);
看起來(lái),似乎并沒(méi)有什么問(wèn)題,但是你想一下,如果你有10個(gè)文件中都使用了這個(gè) class,是不是意味著 _classCallCheck、_defineProperties、_createClass 這些方法被 inject 了10次。這顯然會(huì)導(dǎo)致包體積增大,最關(guān)鍵的是,我們并不需要它 inject 多次。
這個(gè)時(shí)候,就是 @babel/plugin-transform-runtime 插件大顯身手的時(shí)候了,使用 @babel/plugin-transform-runtime 插件,所有幫助程序都將引用模塊 @babel/runtime,這樣就可以避免編譯后的代碼中出現(xiàn)重復(fù)的幫助程序,有效減少包體積。

@babel/plugin-transform-runtime
@babel/plugin-transform-runtime 是一個(gè)可以重復(fù)使用 Babel 注入的幫助程序,以節(jié)省代碼大小的插件。
注意:諸如
Array.prototype.flat()等實(shí)例方法將不起作用,因?yàn)檫@需要修改現(xiàn)有的內(nèi)置函數(shù)(可以使用@babel/polyfill來(lái)解決這個(gè)問(wèn)題) ——> 對(duì)此需要說(shuō)明的是如果你配置的是corejs3,core-js@3現(xiàn)在已經(jīng)支持原型方法,同時(shí)不污染原型。
另外,@babel/plugin-transform-runtime 需要和 @babel/runtime 配合使用。
首先安裝依賴,@babel/plugin-transform-runtime 通常僅在開(kāi)發(fā)時(shí)使用,但是運(yùn)行時(shí)最終代碼需要依賴 @babel/runtime,所以 @babel/runtime 必須要作為生產(chǎn)依賴被安裝,如下 :
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
除了前文所說(shuō)的,@babel/plugin-transform-runtime 可以減少編譯后代碼的體積外,我們使用它還有一個(gè)好處,它可以為代碼創(chuàng)建一個(gè)沙盒環(huán)境,如果使用 @babel/polyfill 及其提供的內(nèi)置程序(例如 Promise ,Set 和 Map ),則它們將污染全局范圍。雖然這對(duì)于應(yīng)用程序或命令行工具可能是可以的,但是如果你的代碼是要發(fā)布供他人使用的庫(kù),或者無(wú)法完全控制代碼運(yùn)行的環(huán)境,則將成為一個(gè)問(wèn)題。
@babel/plugin-transform-runtime 會(huì)將這些內(nèi)置別名作為 core-js 的別名,因此您可以無(wú)縫使用它們,而無(wú)需 polyfill。
修改 .babelrc 的配置,如下:
//.babelrc
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
]
],
"plugins": [
[
"@babel/plugin-transform-runtime"
]
]
}
重新編譯 npm run compiler , 現(xiàn)在,編譯出來(lái)的內(nèi)容為(lib/index.js):
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var Point =
/*#__PURE__*/
function () {
function Point(x, y) {
(0, _classCallCheck2.default)(this, Point);
this.x = x;
this.y = y;
}
(0, _createClass2.default)(Point, [{
key: "getX",
value: function getX() {
return this.x;
}
}]);
return Point;
}();
var cp = new ColorPoint(25, 8);
可以看出,幫助函數(shù)現(xiàn)在不是直接被 inject 到代碼中,而是從 @babel/runtime 中引入。前文說(shuō)了使用 @babel/plugin-transform-runtime 可以避免全局污染,我們來(lái)看看是如何避免污染的。
修改 src/index.js 如下:
let isHas = [1,2,3].includes(2);
new Promise((resolve, reject) => {
resolve(100);
});
編譯出來(lái)的代碼如下(lib/index.js):
"use strict";
require("core-js/modules/es.array.includes");
require("core-js/modules/es.object.to-string");
require("core-js/modules/es.promise");
var isHas = [1, 2, 3].includes(2);
new Promise(function (resolve, reject) {
resolve(100);
});
Array.prototype 上新增了 includes 方法,并且新增了全局的 Promise 方法,污染了全局環(huán)境,這跟不使用 @babel/plugin-transform-runtime 沒(méi)有區(qū)別嘛。

如果我們希望 @babel/plugin-transform-runtime 不僅僅處理幫助函數(shù),同時(shí)也能加載 polyfill 的話,我們需要給 @babel/plugin-transform-runtime 增加配置信息。
首先新增依賴 @babel/runtime-corejs3:
npm install @babel/runtime-corejs3 --save
修改配置文件如下(移除了 @babel/preset-env 的 useBuiltIns 的配置,不然不就重復(fù)了嘛嘛嘛,不信的話,你用 async/await 編譯下試試咯):
{
"presets": [
[
"@babel/preset-env"
]
],
"plugins": [
[
"@babel/plugin-transform-runtime",{
"corejs": 3
}
]
]
}
然后重新編譯,看一下,編譯出來(lái)的結(jié)果(lib/index.js):
"use strict";
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));
var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes"));
var _context;
var isHas = (0, _includes.default)(_context = [1, 2, 3]).call(_context, 2);
new _promise.default(function (resolve, reject) {
resolve(100);
});
可以看出,沒(méi)有直接去修改 Array.prototype,或者是新增 Promise 方法,避免了全局污染。如果上面 @babel/plugin-transform-runtime 配置的 core-js 是 "2",其中不包含實(shí)例的 polyfill 需要單獨(dú)引入。
劃重點(diǎn):如果我們配置的 `corejs` 是 `3` 版本,那么不管是實(shí)例方法還是全局方法,都不會(huì)再污染全局環(huán)境。
看到這里,不知道大家有沒(méi)有這樣一個(gè)疑問(wèn)?給 @babel/plugin-transform-runtime 配置 corejs 是如此的完美,既可以將幫助函數(shù)變成引用的形式,又可以動(dòng)態(tài)引入 polyfill,并且不會(huì)污染全局環(huán)境。何必要給 @babel/preset-env 提供 useBuiltIns 功能呢,看起來(lái)似乎不需要呀。
帶著這樣的疑問(wèn),我新建了幾個(gè)文件(內(nèi)容簡(jiǎn)單且基本一致,使用了些新特性),然后使用 webpack 構(gòu)建,以下是我對(duì)比的數(shù)據(jù):
| .babelrc 配置 | webpack mode production |
|------|------------|------------|
| 不使用 @babel/plugin-transform-runtime | 36KB |
| 使用@babel/plugin-transform-runtime,并配置參數(shù) corejs: 3。不會(huì)污染全局環(huán)境 | 37KB |
| 使用@babel/plugin-transform-runtime,不配置 corejs | 22KB |
我猜測(cè)是 @babel/runtime-corejs3/XXX 的包本身比 core-js/modules/XXX 要大一些~

插件/預(yù)設(shè)補(bǔ)充知識(shí)
插件的排列順序很重要!!!
如果兩個(gè)轉(zhuǎn)換插件都將處理“程序(Program)”的某個(gè)代碼片段,則將根據(jù)轉(zhuǎn)換插件或 preset 的排列順序依次執(zhí)行。
插件在 Presets 前運(yùn)行。
插件順序從前往后排列。
Preset 順序是顛倒的(從后往前)。
例如:
{
"plugins": ["@babel/plugin-proposal-class-properties", "@babel/plugin-syntax-dynamic-import"]
}
先執(zhí)行 @babel/plugin-proposal-class-properties,后執(zhí)行 @babel/plugin-syntax-dynamic-import
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
preset 的執(zhí)行順序是顛倒的,先執(zhí)行 @babel/preset-react, 后執(zhí)行 @babel/preset-env。
插件參數(shù)
插件和 preset 都可以接受參數(shù),參數(shù)由插件名和參數(shù)對(duì)象組成一個(gè)數(shù)組。preset 設(shè)置參數(shù)也是這種格式。
如:
{
"plugins": [
[
"@babel/plugin-proposal-class-properties",
{ "loose": true }
]
]
}
插件的短名稱
如果插件名稱為 @babel/plugin-XXX,可以使用短名稱@babel/XXX :
{
"plugins": [
"@babel/transform-arrow-functions" //同 "@babel/plugin-transform-arrow-functions"
]
}
如果插件名稱為 babel-plugin-XXX,可以使用端名稱 XXX,該規(guī)則同樣適用于帶有 scope 的插件:
{
"plugins": [
"newPlugin", //同 "babel-plugin-newPlugin"
"@scp/myPlugin" //同 "@scp/babel-plugin-myPlugin"
]
}
創(chuàng)建 Preset
可以簡(jiǎn)單的返回一個(gè)插件數(shù)組
module.exports = function() {
return {
plugins: [
"A",
"B",
"C"
]
}
}
preset中也可以包含其他的preset,以及帶有參數(shù)的插件。
module.exports = function() {
return {
presets: [
require("@babel/preset-env")
],
plugins: [
[require("@babel/plugin-proposal-class-properties"), { loose: true }],
require("@babel/plugin-proposal-object-rest-spread")
]
}
}
配置文件
Babel 支持多種格式的配置文件。這部分內(nèi)容補(bǔ)充了解下即可,誰(shuí)管你用哪種配置文件,只要你的配置是OK的就可以了(敷衍)~
所有的 Babel API 參數(shù)都可以被配置,但是如果該參數(shù)需要使用的 JS 代碼,那么可能需要使用 JS 代碼版的配置文件。
根據(jù)使用場(chǎng)景可以選擇不同的配置文件:
如果希望以編程的方式創(chuàng)建配置文件或者希望編譯 node_modules 目錄下的模塊:那么 babel.config.js 可以滿足你的需求。
如果只是需要一個(gè)簡(jiǎn)單的并且用于單個(gè)軟件包的配置:那么 .babelrc 即可滿足你的需求。
babel.config.js
在項(xiàng)目根目錄下創(chuàng)建一個(gè)名為 babel.config.js 的文件。
module.exports = function(api) {
api.cache(true);
const presets = [...];
const plugins = [...];
return {
presets,
plugins
};
}
具體的配置可以查看:babel.config.js 文檔
.babelrc
在項(xiàng)目根目錄下創(chuàng)建一個(gè)名為 .babelrc 的文件:
{
"presets": [],
"plugins": []
}
具體的配置可以參考 .babelrc 文檔
package.json
可以將 .babelrc 中的配置信息作為 babel 鍵(key) 添加到 package.json 文件中:
{
"name": "my-package",
"babel": {
"presets": [],
"plugins": []
}
}
.babelrc.js
與 .babelrc 配置相同,但是可以使用JS編寫(xiě)。
//可以在其中調(diào)用 Node.js 的API
const presets = [];
const plugins = [];
module.exports = { presets, plugins };

不知道是否全面,不過(guò)真的寫(xiě)不動(dòng)了(如有不全,后續(xù)再補(bǔ)充)~就醬~如果有錯(cuò)誤,歡迎指正。
參考鏈接
babel文檔
babel 7 的使用的個(gè)人理解
一口(很長(zhǎng)的)氣了解 babel
core-js@3帶來(lái)的驚喜
