[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 来进行转码。