Babel是一個JavaScript編譯器,不僅能將當前運行環境不支持的JavaScript語法(例如ES6、ES7等)編譯成向下兼容的可用語法(例如ES3或ES5),這其中會涉及新語法的轉換和缺失特性的修補;還支持語法擴展,從而能隨時隨地的使用JSX、TypeScript等語法。目前最新版本是7.4,自從6.0以來,Babel被分解的更加模塊化,各種轉譯功能都以插件的形式分離出來,可按自己的需求,靈活配置。
  在7.0版本中,對Babel的包做了一次大調整,統一改成域級包,將原先以“babel-”為前綴的包遷移到@babel的命名空間,例如@babel/core、@babel/cli等。這種模塊化的設計,既能區分是否是官方發布的,也能避免命名沖突。
## 一、@babel/core
  如果要以編程的方式使用Babel,那么可以通過@babel/core實現,安裝命令如下所示。
~~~
npm install --save-dev @babel/core
~~~
  在引入該包后,就能在JavaScript文件中直接編譯代碼、文件或AST。以編譯代碼為例,可選擇的方法有三個,如下所示。
~~~
var babel = require("@babel/core");
babel.transform(code, options, function(err, result) {
console.log(result); // => { code, map, ast }
});
babel.transformSync(code, options) // => { code, map, ast }
babel.transformAsync(code, options) // => Promise<{ code, map, ast }>
~~~
  雖然transform()用于異步編譯,transformSync()用于同步編譯,但是它們得到的結果相同,都是一個由轉換后的代碼(code)、源碼映射信息(map)和抽象語法樹(ast)組成的對象。而transformAsync()得到的結果與前面兩個方法不同,它返回一個包含code、map和ast的Promise對象。這些方法的第二個options參數,可用于傳遞配置信息,具體可參考[官方文檔](https://www.babeljs.cn/docs/options)。
  如果要編譯文件或AST,那么也有類似的三個方法可供選擇。除了能編譯之外,利用這個包還能把代碼解析成AST(即傳入一段代碼,得到一個AST)以便其它插件對其進行語法分析,可使用的方法有parse()、parseSync()和parseAsync()。
## 二、@babel/cli
  @babel/cli是一個Babel內置的命令行工具,可通過命令來編譯文件,其安裝如下所示。
~~~
npm install --save-dev @babel/cli
~~~
  假設有一個src.js文件,其內容如下代碼所示,用到了ES6中的箭頭函數。
~~~
let fn = () => true;
~~~
  在運行下面的命令后,可以看到輸出和輸入的代碼相同,這是因為此時還未指定任何轉譯插件。
~~~
./node_modules/.bin/babel src.js
~~~
  如果當前的npm版本是5.2以上,那么可將./node\_modules/.bin縮短為npx,如下所示。
~~~
npx babel src.js
~~~
  在babel中,有多個可用的參數,下面只列舉其中的四個,其余參數的用法可參考[官方文檔](https://www.babeljs.cn/docs/babel-cli)。
~~~
npx babel src --out-dir dist
npx babel src.js --out-file dist.js
npx babel src.js --out-file dist.js --plugins=@babel/plugin-transform-classes,@babel/transform-modules-amd
npx babel src.js --out-file dist.js --presets=@babel/preset-env,@babel/flow
~~~
  (1)“--out-dir”參數可編譯整個src目錄下的文件并輸出到dist目錄中。
  (2)“--out-file”參數可編譯src.js文件并輸出到dist.js文件中。
  (3)“--plugins”參數可指定編譯過程中要使用的插件,多個插件用逗號隔開。
  (4)“--presets”參數可指定編譯過程中要使用的預設,多個預設用逗號隔開。
  在package.json文件中,可以通過scripts字段聲明腳本命令。如果想要簡化babel命令,那么可以將它們遷移到scripts字段中,如下所示。
~~~
{
"scripts": {
"compile": "npx babel src.js --out-file dist.js"
}
}
~~~
  現在要編譯目錄的話,只要執行下面的這條npm命令即可。
~~~
npm run compile
~~~
## 三、配置
  在Babel中,可以將各種命令的參數集中到一個配置文件中,而可配置的文件包括babel.config.js、.babelrc和package.json。
**1)babel.config.js**
  這是Babel 7最新引入的配置文件,存在于根目錄中(即package.json文件所在的目錄)。它不僅能以編程的方式聲明全局生效的[配置參數](https://www.babeljs.cn/docs/config-files#project-wide-configuration),還能利用overrides字段對不同的子目錄進行針對性的配置,從而就能避免為相關目錄創建一個.babelrc文件了。在下面的示例中,為test目錄單獨配置了預設(presets)。
~~~
module.exports = {
presets: [...],
overrides: [{
test: ["./test"],
presets: [...]
}]
};
~~~
  當運行下面兩條命令進行編譯時,第一條讀取的是最外層的預設,第二條讀取的是overrides中的預設。
~~~
npx babel src.js
npx babel ./test/src.js
~~~
**2).babelrc**
  babel.config.js并不是.babelrc的替代品,.babelrc文件用于局部配置(如下代碼所示),可放置于所有目錄中。如果當前目錄沒有.babelrc文件,那么就會往上查找直至找到為止。
~~~
{
"presets": [...]
}
~~~
  注意,如果.babelrc的后綴是“.js”(即.babelrc.js),那么在文件中可以通過JavaScript配置參數。
**3)package.json**
  在package.json文件中,可以聲明一個babel字段,其值就是.babelrc文件中的[配置參數](https://www.babeljs.cn/docs/config-files#file-relative-configuration),如下所示。
~~~
{
"babel": {
"presets": [...]
}
}
~~~
  注意,不能讓.babelrc和聲明過babel字段的package.json處在相同的目錄中。
**4)配置函數或方法**
  前面三種都是用單獨的文件來配置Babel的參數,其實還可以通過相關的函數或方法來達到相同地目的。在下面的示例中,引入gulp-babel包后就能通過得到的babel()函數來配置Babel的參數。
~~~
let babel = require("gulp-babel");
babel({
presets: [...]
});
~~~
## 四、插件
  Babel的編譯可分為三個階段:解析、轉換和生成,而插件(Plugin)在轉換過程中起到了至關重要的作用。借助Babel的插件可將解析完成的AST按照特定的要求進行處理,然后再輸出生成的代碼。
**1)插件類型**
  Babel中的插件分為兩種類型:語法和轉換。語法插件只允許Babel解析成特定類型的語法,例如JSX、Flow等。轉換插件常用來編譯ES6、ES7、React和Modules等,由于能自動開啟相應的語法插件,因此不用顯式的指定兩種插件。以之前的箭頭函數為例,如果要將其編譯成所有瀏覽器都能識別的形式,那么就得安裝相應的插件,如下所示。
~~~
npm install --save-dev @babel/plugin-transform-arrow-functions
~~~
  在.babelrc文件中的配置如下所示。
~~~
{
"plugins": ["@babel/plugin-transform-arrow-functions"]
}
~~~
  在配置文件中,不僅能指定插件的相對或絕對路徑,還可省略插件名稱中的“babel-plugin-”前綴。假設有個插件名叫@org/babel-plugin-name,那么它的相對路徑和簡寫名稱如下所示。
~~~
{
"plugins": [
"../@org/babel-plugin-name",
"@org/name"
]
}
~~~
**2)執行順序**
  插件的執行順序會受配置時所處的位置的影響,具體規則如下所列,其中預設是指官方預先設計的一組插件集,本質上仍然是插件。
  (1)插件執行在預設之前。
  (2)插件會按順序從前往后執行。
  (3)預設與插件相反,從后往前執行。
  以下面的配置信息為例,在插件中,先執行@babel/plugin-transform-arrow-functions,后執行@babel/plugin-transform-classes。而在預設中,先執行@babel/preset-react,后執行@babel/preset-env。
~~~
{
"plugins": ["@babel/plugin-transform-arrow-functions", "@babel/plugin-transform-classes"],
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
~~~
**3)插件參數**
  如果要配置插件的參數,那么得在插件名稱后增加一個參數對象,并且寫成數組的形式,如下所示。
~~~
{
"plugins": [
["@babel/plugin-transform-arrow-functions", { "spec": true }],
["@babel/plugin-transform-classes", { "loose": true }]
]
}
~~~
  預設也能接收參數,其寫法與之類似。
## 五、預設
  如果在配置文件中一個一個的聲明插件,那么不僅會讓該文件變得巨大,而且還難免會有所遺漏。官方為了避免此類問題,引入了預設(Preset)的概念。預設類似于生活中的套餐,每個套餐會集合不同的插件,從而能夠一次性安裝各類插件,并且還可共享配置的參數。
**1)@babel/preset-env**
  此預設不僅集合了最新的ES語法(即編譯指定的ES標準),還能配置所要支持的平臺(例如Node、Chrome等)和版本,以及按需加載插件,其安裝命令如下所示。
~~~
npm install --save-dev @babel/preset-env
~~~
  下面是一個預設的配置示例,支持Chrome 58和IE 11,以及超過市場份額5%的瀏覽器。還有許多其它可供選擇的配置參數,具體可翻閱[官方文檔](https://www.babeljs.cn/docs/babel-preset-env)。
~~~
{
presets: [
[
"@babel/preset-env",
{
targets: {
chrome: "58",
ie: "11",
browsers: "> 5%"
}
}
]
]
}
~~~
  除了@babel/preset-env之外,官方還給出了其它實用的預設,例如@babel/preset-flow、@babel/preset-react和@babel/preset-typescript等。
  與插件類似,預設也能指定相對或絕對路徑,只不過它省略的前綴是“babel-preset-”。假設有個預設名叫@org/babel-preset-name,那么它的相對路徑和簡寫名稱如下所示。
~~~
{
"presets": [
"../@org/babel-preset-name",
"@org/name"
]
}
~~~
**2)stage-x**
  這是一組實驗性質的預設,囊括了處于提案階段的標準,TC39委員會將提案分為5個階段,如表2所示。
:-: 
表2 提案的五個階段
  以上4個階段的預設存在著依賴關系,階段靠前的依賴階段靠后的(即數字小的包含數字大的),例如@babel/preset-stage-0依賴@babel/preset-stage-1。
  由于這些提案階段的預設不太穩定,很有可能會被TC39委員會除名或變更,并且混合使用在配置上容易出錯,因此從Babel 7開始,它們都將被廢棄。
## 六、@babel/polyfill
  雖然env預設(@babel/preset-env)能統一JavaScript的新語法(即高版本編譯成低版本),但是無法支持內置的新方法或新對象,例如Promise、Array.of()等。為此,Babel引入了的Polyfill技術(全部打包在@babel/polyfill中),將所缺的特性添加到全局對象中或內置對象的原型上,彌補env預設的不足,從而模擬出完整的ES6+語法和特性。
  @babel/polyfill包含兩個模塊:regenerator-runtime和[core-js](https://github.com/zloirock/core-js),前者用于編譯生成器與異步函數(async和await),后者用于處理其它兼容性問題。@babel/polyfill的安裝命令如下所示,注意,使用的參數是--save而不是--save-dev,因為需要在源碼之前先執行Polyfill。
~~~
npm install --save @babel/polyfill
~~~
**1)useBuiltIns**
  在env預設中,存在一個與@babel/polyfill有緊密聯系的useBuiltIns參數,它有三個關鍵字可供選擇,分別是false、entry和usage,具體說明如下所列。
  (1)false:默認值,不開啟Polyfill,顯式的配置如下所示。
~~~
{
presets: [
["@babel/preset-env", { useBuiltIns: false }]
]
}
~~~
  (2)entry:加載運行環境(可在targets參數中聲明)所需的Polyfill,下面是一個使用Promise的例子。
~~~
require("@babel/polyfill");
new Promise();
~~~
  當useBuiltIns參數的值為entry時,這段代碼在編譯后,就會引入許多不相干的JavaScript文件,造成資源的浪費,如下所示。
~~~
require("core-js/modules/es6.promise");
require("core-js/modules/es6.array.fill");
require("core-js/modules/es6.math.trunc");
require("core-js/modules/es6.string.fixed");
......
new Promise();
~~~
  (3)usage:自動加載源碼所需的Polyfill,仍然以Promise為例,如下代碼所示,不用再顯式的引入@babel/polyfill。
~~~
new Promise();
~~~
  當useBuiltIns參數的值為usage時,這段代碼在編譯后,就會只引入需要的JavaScript文件,如下所示。
~~~
require("core-js/modules/es6.promise");
require("core-js/modules/es6.object.to-string");
new Promise();
~~~
## 七、@babel/runtime
  @babel/runtime與@babel/polyfill類似,也是用來增強env預設的,只是它以非侵入式的輔助函數來填補平臺所沒有的特性,其安裝命令如下所示。
~~~
npm install --save @babel/runtime
~~~
  在Babel 7中還引入了一個@babel/runtime-corejs2,它比@babel/runtime多包含一個core-js模塊,即能夠編譯Promise、Symbol等。接下來以ES6的類為例,如下所示。
~~~
class People {
name() {}
}
~~~
  在將People類編譯后,得到的結果如下所示,省略了三個函數中的邏輯代碼。
~~~
"use strict";
function _classCallCheck(instance, Constructor) { }
function _defineProperties(target, props) { }
function _createClass(Constructor, protoProps, staticProps) { }
var People =
/*#__PURE__*/
function () {
function People() {
_classCallCheck(this, People);
}
_createClass(People, [{
key: "name",
value: function name() {}
}]);
return People;
}();
~~~
**1)@babel/plugin-transform-runtime**
  由于編譯生成的輔助函數會滯留在所使用的文件中,因此文件越多冗余的函數就越多。如果人工分離,那工作量將巨大,因此Babel提供了能自動將它們分離的@babel/plugin-transform-runtime插件,其安裝命令如下所示。
~~~
npm install --save-dev @babel/plugin-transform-runtime
~~~
  仍然以ES6的People類為例,先在配置文件中將其聲明,如下所示。
~~~
{
plugins: ["@babel/plugin-transform-runtime"]
}
~~~
  然后再將類編譯,三個輔助函數就能通過引入的方式得到,如下所示。
~~~
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
~~~
## 八、動態編譯
  在Babel中有兩種方式實現動態編譯,分別是@babel/register和@babel/node,它們的安裝命令如下所示。
~~~
npm install --save-dev @babel/register
npm install --save-dev @babel/node
~~~
  雖然動態編譯省去了手動操作,但是會損耗程序的速度和性能,因此不適合生產環境。
**1)@babel/register**
  在將其安裝后,就會為Node.js的require()函數增加一個鉤子。每當通過require()加載后綴為.es6、.es、.jsx、.mjs或.js的文件時,就能自動對其進行Babel編譯。接下來用一個例子來演示@babel/register的用法,首先創建一個src.js的文件,其代碼如下所示。
~~~
module.exports = () => true;
~~~
  然后創建一個default.js文件,引入@babel/register和之前的src.js,如下所示。
~~~
require("@babel/register");
var code = require("./src.js");
console.log(code.toString());
~~~
  最后在命令行工具中輸入“node default.js”,打印出編譯后的匿名函數,如下所示。
~~~
function () {
return true;
}
~~~
  在@babel/register中,如果需要使用Polyfill,那么得逐個引入。還要注意,它默認不會編譯node\_modules目錄中的模塊,但是可以通過ignore參數修改忽略規則來覆蓋其行為。
  ignore參數是一個由正則表達式和函數組成的數組(如下代碼所示),如果要讓文件不被編譯,那么得將其路徑與正則表達式匹配或函數返回true,其中函數的參數就是文件路徑。
~~~
require("babel-register")({
ignore: [
/regex/,
function(filepath) {
return true;
}
]
});
~~~
  另外還有一個only參數,其值也是一個數組,同樣也用來設置文件不被編譯的條件。只是其規則與ignore參數正好相反,即路徑與正則表達式不匹配或函數返回false時,才不編譯。
  @babel/register還可以接收其它參數,例如extensions、cache等,并且會合并配置文件中的信息,作為其參數傳遞進來。
**2)@babel/node**
  在Babel 7之前,@babel/cli會自帶babel-node命令,但之后,官方將其拆成一個單獨的包:@babel/node。與@babel/register不同,@babel/node不需要調整源碼,直接一個命令就能實現動態編譯,如下代碼所示,其中src就是之前的src.js文件。
~~~
npx babel-node src
~~~
*****
> 原文出處:
[博客園-前端利器躬行記](https://www.cnblogs.com/strick/category/1472499.html)
[知乎專欄-前端利器躬行記](https://zhuanlan.zhihu.com/pwtool)
已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎瀏覽。

推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、擴展運算符和剩余參數
- 3、解構
- 4、模板字面量
- 5、對象字面量的擴展
- 6、Symbol
- 7、代碼模塊化
- 8、數字
- 9、字符串
- 10、正則表達式
- 11、對象
- 12、數組
- 13、類型化數組
- 14、函數
- 15、箭頭函數和尾調用優化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、類
- 21、類的繼承
- 22、Promise
- 23、Promise的靜態方法和應用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基礎實踐
- 3、WebRTC視頻通話
- 4、Web音視頻基礎
- CSS進階
- 1、CSS基礎拾遺
- 2、偽類和偽元素
- 3、CSS屬性拾遺
- 4、浮動形狀
- 5、漸變
- 6、濾鏡
- 7、合成
- 8、裁剪和遮罩
- 9、網格布局
- 10、CSS方法論
- 11、管理后臺響應式改造
- React
- 1、函數式編程
- 2、JSX
- 3、組件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表單
- 8、樣式
- 9、組件通信
- 10、高階組件
- 11、Redux基礎
- 12、Redux中間件
- 13、React Router
- 14、測試框架
- 15、React Hooks
- 16、React源碼分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基礎
- 4、webpack進階
- 5、Git
- 6、Fiddler
- 7、自制腳手架
- 8、VSCode插件研發
- 9、WebView中的頁面調試方法
- Vue.js
- 1、數據綁定
- 2、指令
- 3、樣式和表單
- 4、組件
- 5、組件通信
- 6、內容分發
- 7、渲染函數和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、數據類型
- 2、接口
- 3、類
- 4、泛型
- 5、類型兼容性
- 6、高級類型
- 7、命名空間
- 8、裝飾器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系統和網絡
- 3、命令行工具
- 4、自建前端監控系統
- 5、定時任務的調試
- 6、自制短鏈系統
- 7、定時任務的進化史
- 8、通用接口
- 9、微前端實踐
- 10、接口日志查詢
- 11、E2E測試
- 12、BFF
- 13、MySQL歸檔
- 14、壓力測試
- 15、活動規則引擎
- 16、活動配置化
- 17、UmiJS版本升級
- 18、半吊子的可視化搭建系統
- 19、KOA源碼分析(上)
- 20、KOA源碼分析(下)
- 21、花10分鐘入門Node.js
- 22、Node環境升級日志
- 23、Worker threads
- 24、低代碼
- 25、Web自動化測試
- 26、接口攔截和頁面回放實驗
- 27、接口管理
- 28、Cypress自動化測試實踐
- 29、基于Electron的開播助手
- Node.js精進
- 1、模塊化
- 2、異步編程
- 3、流
- 4、事件觸發器
- 5、HTTP
- 6、文件
- 7、日志
- 8、錯誤處理
- 9、性能監控(上)
- 10、性能監控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 監控系統
- 1、SDK
- 2、存儲和分析
- 3、性能監控
- 4、內存泄漏
- 5、小程序
- 6、較長的白屏時間
- 7、頁面奔潰
- 8、shin-monitor源碼分析
- 前端性能精進
- 1、優化方法論之測量
- 2、優化方法論之分析
- 3、瀏覽器之圖像
- 4、瀏覽器之呈現
- 5、瀏覽器之JavaScript
- 6、網絡
- 7、構建
- 前端體驗優化
- 1、概述
- 2、基建
- 3、后端
- 4、數據
- 5、后臺
- Web優化
- 1、CSS優化
- 2、JavaScript優化
- 3、圖像和網絡
- 4、用戶體驗和工具
- 5、網站優化
- 6、優化閉環實踐
- 數據結構與算法
- 1、鏈表
- 2、棧、隊列、散列表和位運算
- 3、二叉樹
- 4、二分查找
- 5、回溯算法
- 6、貪心算法
- 7、分治算法
- 8、動態規劃
- 程序員之路
- 大學
- 2011年
- 2012年
- 2013年
- 2014年
- 項目反思
- 前端基礎學習分享
- 2015年
- 再一次項目反思
- 然并卵
- PC網站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端學習之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 2024年
- 日志
- 2020