[TOC]
Babel是一個廣泛使用的ES6轉碼器,可以將ES6代碼轉為ES5代碼,從而在現有環境執行。
ECMAScript 6是JavaScript語言的下一代標準。因為當前版本的ES6是在2015年發布的,所以又稱ECMAScript 2015。但是目前瀏覽器對es6不完全兼容,需要借住babel編譯。
### 配置文件
通過 .babelrc 來配置Babel 轉碼規則,放在項目根目錄下 /.babelrc
~~~
{
"presets": ["es2015", "react", "stage-2"],
"plugins": []
}
~~~
上面配置了 Babel 轉碼規則為,將ES6的語法轉為ES2015的轉碼,支持React的JSX語法轉碼,使用 stage-2 模式轉碼(相關模式下面會詳細介紹)
Babel 通過三個步驟 語法解析-轉碼-生成文件來編譯Javascript。
Babel 是通過一系列插件來完成對JS的編譯。
stage-x 是對非標準或未定案的標準的API的轉碼實現
### Presets
presets 是Babel內置的一些預設,是一系列插件的組合
### 官方預設 Official Presets
1. env 根據配置的環境自動加載相關的插件
2. es2015 將ES6的語法轉碼為ES5的語法
3. es2016 冪運算語法糖插件 2**3 => 2*2*2
4. es2017 Async / Await / Generator
5. react 支持React編譯
6. flow 支持靜態類型檢測編譯
### 插件
#### 支持的模塊加載Modules
1. es2015-modules-amd 瀏覽器端的AMD模塊加載管理
2. es2015-modules-commonjs 服務端的同步模塊加載管理
3. es2015-modules-systemjs ES6的模塊管理方式
4. es2015-modules-umd 兼容AMD CMD的模塊管理方式
#### 實驗性插件Experimental
1. async-generator-functions 支持將ES7的 async await 函數轉換為ES6的 generator 函數
2. async-to-module-method 支持將ES7的 async await 函數轉換為 bluebird 語法以兼容低版本[需要配置]
3. class-properties 類的靜態屬性與方法
4. decorators 類的裝飾器 @connect
5. do-expressions 支持模板中使用 if/else
6. export-extensions 模塊導出的擴展
7. function-bind 支持通過 :: 來綁定上下文
8. object-rest-spread 支持使用 … 將對象展開
9. 優化編譯插件 Minification
#### 優化編譯插件 Minification
1. inline-environment-variables 簡化環境變量
2. inline-consecutive-adds 簡化對象與數據定義
3. merge-sibling-variables 將多次變量申明,合并為一次
4. minify-booleans 將true 和 false 轉換為 !0 和 !1
5. minify-constant-folding 優化數字與字符串的計算: a = 2 * 4 => a = 8;
6. minify-dead-code-elimination 簡化代碼,去掉冗余的代碼片段
7. minify-flip-comparisons 優化 Gzip 的壓縮算法
8. minify-guarded-expressions 優化 && 表達式
9. minify-infinity Infinity => 1/0
10. minify-mangle-names 簡化局部變量的名稱
11. minify-numeric-literals 將大數字轉成科學計算法表示 1000 => 1e3
12. minify-replace 自定義在編譯過程需要縮短變量的變量
13. minify-simplify 優化if else 語句、undefined => void 0, Number(foo)=> +foo, foo[‘bar’]=>foo.bar
14. minify-type-constructors 簡化使用基本類型構造的變量
15. node-env-inline node中將環境變量轉換成行內變量
16. property-literals 對象字符屬性轉成唯一屬性,關鍵字除外
17. regexp-constructors 如果Regext()參數是字符串,則轉成字面量
18. remove-console 去掉console語句
19. remove-debugger 去掉 debugger 語句
20. simplify-comparison-operators 如果兩邊的類型一致,則將 === 轉換為 ==
21. undefined-to-void undefined => void 0
#### React
1. react-constant-elements 靜態模板轉常量
2. react-display-name 為組件添加 displayName 屬性
3. react-inline-elements 使用性能更好的 babelHelpers.jsx() 替換 React.createElement() 轉換模板
4. react-jsx JSX模板轉為JS語句
5. react-jsx-compat JSX模板轉成 0.12之前語法
6. react-jsx-self 添加__self屬性標識本身信息
7. react-jsx-source 添加源碼信息到JSX模板
#### Other
1. eval 轉碼eval()中的ES6語法
2. flow-comments 轉碼后添加類型檢查注釋
3. flow-strip-types 類型檢測轉成ES5語法
4. jscript 函數表達式轉換為申明式函數
5. object-assign 轉碼 Object.assign 為ES5的 _extends()語法
6. object-set-prototype-of-to-assign 轉碼 setPrototypeOf 為 _defautls() 方法
7. proto-to-assign 使用_defaults() 替換 __proto__ 為對象擴展屬性
8. regenerator 轉碼generator語法
9. runtime 提供一些工具方法如_extends 來幫助轉碼
10. strict-mode 添加 ‘use strict’;
#### 語法解析插件Syntax
語法解析插件,只提供編譯過程中的語法解析,并不轉碼,轉碼需要想關的轉碼插件支持,在使用相關轉碼插件時,會自動安裝使用相關語法解析插件,不需要手動安裝配置。
1. async-functions
2. async-generators
3. class-constructor-call
4. class-properties
5. decorators
6. do-expressions
7. exponentiation-operator
8. export-extensions
9. flow
10. function-bind
11. jsx
12. object-rest-spread
13. trailing-function-commas
#### ES2015
默認情況下,Babel 自帶了一組 ES2015 語法轉化器。這些轉化器能讓你現在就使用最新的 JavaScript 語法,而不用等待瀏覽器提供支持。(每個一支持的特性都有獨立的插件可以使用)
~~~
check-es2015-constants 檢查const的使用規則
transform-es2015-arrow-functions 箭頭函數 ()=>{}
transform-es2015-block-scoped-functions 確實塊級作用域下函數重復申明
transform-es2015-block-scoping 塊級作用域 let & const
transform-es2015-classes 類 class Header {}
transform-es2015-computed-properties 動態屬性名 obj[getKey('enabled')] = true;
transform-es2015-destructuring 解構 let [a,b] = [0,0]
transform-es2015-duplicate-keys 防止對象屬性重復
transform-es2015-for-of 迭代器遍歷
transform-es2015-function-name 支持通過 func.name 獲取函數名稱
transform-es2015-literals 將ES2015的整數和unicode文字編譯為ES5
transform-es2015-modules-commonjs 支持 commonjs 模塊導入導出方式
transform-es2015-object-super ES6對象編譯為ES5對象
transform-es2015-parameters 支持函數參數設置默認值 function foo (a=1,b){}
transform-es2015-shorthand-properties 屬性簡寫 var d = {a, b}
transform-es2015-spread 展開符 ...
transform-es2015-sticky-regex 支持正則的粘性特性
transform-es2015-template-literals 支持ES6的字符串模板 `/a/${c}`
transform-es2015-typeof-symbol 提升typeof的兼容性
transform-es2015-unicode-regex 支持正則匹配 unicode 字符
transform-regenerator 編譯 genenerator 語法,并不轉碼(需要 babel-polyfill 或 regenerator runtime 的支持)
~~~
Strick regex 是否可以從指定的 lastIndex 開始搜索
Flow - JS靜態類型檢查工具
~~~
function foo(one: any, two: number, three?): string {}
~~~
### 常用ES6語法與轉碼對比
Arrow Function、Class、Funciton bind、Let、Const、Template、Modules、Destructuring、Spreade
#### 函數
~~~
//in
var nums = evens.map((v, i) => v + i);
//out
var nums = evens.map(function (v, i) {
return v + i;
});
//保持方法的上下文
//in
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
};
//out
var bob = {
_name: "Bob",
_friends: [],
printFriends: function printFriends() {
var _this = this;
this._friends.forEach(function (f) {
return console.log(_this._name + " knows " + f);
});
}
};
//in
function foo(one: any, two: number, three?): string {}
//out
function foo(one, two, three) {}
~~~
#### 類
~~~
//in
class Bork {
//Property initializer syntax
instanceProperty = "bork";
boundFunction = () => {
return this.instanceProperty;
}
//Static class properties
static staticProperty = "babelIsCool";
static staticFunction = function() {
return Bork.staticProperty;
}
}
// out
"use strict";
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Bork = function Bork() {
var _this = this;
_classCallCheck(this, Bork);
this.instanceProperty = "bork";
this.boundFunction = function () {
return _this.instanceProperty;
};
}
//Property initializer syntax
//Static class properties
;
Bork.staticProperty = "babelIsCool";
Bork.staticFunction = function () {
return Bork.staticProperty;
};
~~~
#### Let + Const
~~~
//in
function f() {
{
// 塊級作用域下的變量
let x;
{
// 塊級作用域下的const常量
const x = "sneaky";
x = 'foo'; // 報錯,常量不允許修改
}
x = "bar";
let x= 'foo'; // 報錯,變量重復定義
}
}
//out
"use strict";
function f() {
{
// 塊級作用域下的變量
var x = void 0;
{
// 塊級作用域下的const常量
var _x = "sneaky";
}
x = "bar";
}
}
~~~
#### 屬性與方法的語法糖轉換
~~~
var o = { a, b, c }; // var o = { a: a, b: b, c: c };
//in
var cat = {
getName() {
return name;
}
};
//out
var cat = {
getName: function () {
return name;
}
};
~~~
#### 模板字符串
~~~
//in
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
//out
var name = "Bob",
time = "today";
"Hello " + name + ", how are you " + time + "?";
~~~
#### 解構賦值
~~~
// 從數組中解構
//in
var [a, b,c] = [1,2,3];
//out
var a = 1,
b = 2,
c = 3;
// 從函數返回值中解構
//in
var {op, lhs, rhs} = getASTNode()
//out
var _getASTNode2 = getASTNode(),
op = _getASTNode2.op,
lhs = _getASTNode2.lhs,
rhs = _getASTNode2.rhs;
//從實參中解構與默認值配置
//in
function r({x, y, w = 10, h = 10}) {
return x + y + w + h;
}
r({x:1, y:2}) === 23
//out
function r(_ref4) {
var x = _ref4.x,
y = _ref4.y,
_ref4$w = _ref4.w,
w = _ref4$w === undefined ? 10 : _ref4$w,
_ref4$h = _ref4.h,
h = _ref4$h === undefined ? 10 : _ref4$h;
return x + y + w + h;
}
r({ x: 1, y: 2 }) === 23;
~~~
#### 模塊內容導出與導入
~~~
//in
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
//out
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.sum = sum;
function sum(x, y) {
return x + y;
}
var pi = exports.pi = 3.141593;
//in
import * as math from "lib/math";
console.log("2π = " + math.sum(math.pi, math.pi));
//out
var _math = require("lib/math");
var math = _interopRequireWildcard(_math);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
console.log("2π = " + math.sum(math.pi, math.pi)); // app.js
~~~
### Babel-polyfill
針對默認不轉換的API,需要另外添加一個Polyfill,或針對某個API添加插件。
Babel 默認只轉換新的JavaScript句法(syntax),而不轉換新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局對象,以及一些定義在全局對象上的方法(比如Object.assign)都不會轉碼。
舉例來說,ES6在 Array 對象上新增了 Array.from 方法。Babel就不會轉碼這個方法。如果想讓這個方法運行,必須使用 babel-polyfill,為當前環境提供一個墊片。
~~~
npm install --save-dev babel-polyfill
~~~
babel-polyfill 提提供了對新的標準API的支持,兼容老的版本。
下列特性需要使用 babel-polyfill 才能轉碼,或者單獨引用相關插件
~~~
// 數組操作
ArrayBuffer
Array.from
Array.of
Array#copyWithin
Array#fill
Array#find
Array#findIndex
Function#name
// 數學方法
Math.acosh
Math.hypot
Math.imul
// 數字類型的擴展方法
Number.isNaN
Number.isInteger
// 對象的擴展方法
Object.assign
Object.getOwnPropertyDescriptors
Object.is
Object.entries
Object.values
Object.setPrototypeOf
Promise
Reflect
RegExp#flags
// 字符串的擴展方法
String#codePointAt
String#endsWith
String.fromCodePoint
String#includes
String.raw
String#repeat
String#startsWith
String#padStart
String#padEnd
// 新擴展的數據類型
Map
Set
Symbol
WeakMap
WeakSet
~~~
使用
~~~
import 'babel-polyfill';
// 或者
require('babel-polyfill');
~~~
### Stage說明
Babel的轉換級別依據 ES7不同階段的語法提案設置了4個階段:stage-0 、stage-1、stage-2、stage-3,使用時選裝一個。(0的級別最高,包含的轉碼插件最多,往后越來越少)
#### stage-0
提供ES7的支持
包含 stage-1、stage-2、stage-3 的內容
特有的插件
* transform-do-expressions
* transform-function-bind
transform-do-expressions 支持 模板中使用if else
~~~
<div className="parents">
{
do {
if(color == 'blue') {
<BlueComponent/>;
}else if(color == 'red') {
<RedComponent/>;
}else {
<GreenComponent/>; }
}
}
}
</div>
~~~
transform-function-bind 綁定上下文
~~~
// 語法
obj::func // func.bind(obj)
obj::func(val) // func.call(obj, val)
::obj.func(val) // func.call(obj, val)
// 基本用法
const box = {
weight: 2,
getWeight() { return this.weight; },
};
const { getWeight } = box;
console.log(box.getWeight()); // prints '2'
const bigBox = { weight: 10 };
console.log(bigBox::getWeight()); // prints '10'
// Can be chained:
function add(val) { return this + val; }
console.log(bigBox::getWeight()::add(5)); // prints '15'
// 處理Nodelist(Array Like 類型)
const { map, filter } = Array.prototype;
let sslUrls = document.querySelectorAll('a')
::map(node => node.href)
::filter(href => href.substring(0, 5) === 'https');
console.log(sslUrls);
// is equivalent to
const { map, filter } = Array.prototype;
let sslUrls = document.querySelectorAll('a');
sslUrls = map.call(sslUrls, node => node.href);
sslUrls = filter.call(sslUrls, href => href.substring(0, 5) === 'https');
console.log(sslUrls);
//綁定自身
$('.some-link').on('click', ::view.reset);
// is equivalent to:
$('.some-link').on('click', view.reset.bind(view));
~~~
#### stage-1
包含 stage-2 、stage-3
提供對 類的靜態、實例屬性和方法的支持 以及 對模塊導入方式的擴展
特有插件
* transform-class-properties
* transform-export-extensions
transform-class-properties 支持
~~~
class Bork {
//Property initializer syntax
instanceProperty = "bork";
boundFunction = () => {
return this.instanceProperty;
}
//Static class properties
static staticProperty = "babelIsCool";
static staticFunction = function() {
return Bork.staticProperty;
}
}
~~~
transform-export-extensions 擴展export導出方式
~~~
export * as ns from 'mod';
export v from 'mod';
~~~
#### stage-2
包含 stage-3
提供尾逗號函數功能 及 支持 Object 使用延展符展開
特有插件
* syntax-trailing-function-commas
* transform-object-reset-spread
syntax-trailing-function-commas 添加行尾逗號,減少文件的改動
~~~
function clownPuppiesEverywhere(
param1,
param2,
) { /* ... */ }
clownPuppiesEverywhere(
'foo',
'bar',
);
~~~
transform-object-reset-spread 支持使用展開符展開對象
~~~
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }
// 屬性展開
let n = { x, y, ...z };
console.log(n); // { x: 1, y: 2, a: 3, b: 4 }
~~~
#### stage-3
提供對ES7的async和await的支持 及提供冪操作的語法糖
特有插件
* transform-async-to-generator
* transform-exponentiation-operator
transform-async-to-generator 轉碼ES7的 async與await
~~~
//in
async function* agf() {
await 1;
yield 2;
}
//out
var _asyncGenerator = ...
let agf = (() => {
var _ref = _asyncGenerator.wrap(function* () {
yield _asyncGenerator.await(1);
yield 2;
});
return function agf() {
return _ref.apply(this, arguments);
};
})();
~~~
transform-exponentiation-operator 支持冪運算語法糖
~~~
// x ** y
let squared = 2 ** 2;
// 相當于: 2 * 2
let cubed = 2 ** 3;
// 相當于: 2 * 2 * 2
~~~
為了防止某些實驗中的標準在未來不能定案,一般使用 stage-1 或 stage-2 來進行轉碼。