Aitter's Blog

Babel使用入门

Babel是一个广泛使用的ES6转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。
ECMAScript 6是JavaScript语言的下一代标准。因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015。但是目前浏览器对es6不完全兼容,需要借住babel编译。

目录

  • 配置文件

  • 预设

    • 官方预设
  • 插件

    • 模块支持
    • 实验性插件
    • 优化编译
    • React
    • 其它
  • ES2015

  • 常用ES6语法与转码对比

    • 函数
    • let & const
    • 解构赋值
    • 模板字符串
    • 模块导入导出
    • 属性与方法简写
  • Babel-polyfill

  • Stage说明

配置文件

通过 .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

  • env 根据配置的环境自动加载相关的插件
  • es2015 将ES6的语法转码为ES5的语法
  • es2016 幂运算语法糖插件 2**3 => 2*2*2
  • es2017 Async / Await / Generator
  • react 支持React编译
  • flow 支持静态类型检测编译

插件

支持的模块加载Modules

实验性插件Experimental

优化编译插件 Minification

React

Other

语法解析插件Syntax

语法解析插件,只提供编译过程中的语法解析,并不转码,转码需要想关的转码插件支持,在使用相关转码插件时,会自动安装使用相关语法解析插件,不需要手动安装配置。

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) {}

class SkinnedMesh extends THREE.Mesh {
constructor(geometry, materials) {
super(geometry, materials);
this.idMatrix = SkinnedMesh.defaultMatrix();
this.bones = [];
this.boneMatrices = [];
//...
}
update(camera) {
//...
super.update();
}
static defaultMatrix() {
return new THREE.Matrix4();
}
}

类的编译结果%20%7B%0A%20%20%20%20super(geometry%2C%20materials)%3B%0A%0A%20%20%20%20this.idMatrix%20%3D%20SkinnedMesh.defaultMatrix()%3B%0A%20%20%20%20this.bones%20%3D%20%5B%5D%3B%0A%20%20%20%20this.boneMatrices%20%3D%20%5B%5D%3B%0A%20%20%20%20%2F%2F…%0A%20%20%7D%0A%20%20update(camera)%20%7B%0A%20%20%20%20%2F%2F…%0A%20%20%20%20super.update()%3B%0A%20%20%7D%0A%20%20static%20defaultMatrix()%20%7B%0A%20%20%20%20return%20new%20THREE.Matrix4()%3B%0A%20%20%7D%0A%7D%0A)

//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 + "?";

更多解析参考%3B%0A)

解构赋值

// 从数组中解构
//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-0stage-1stage-2stage-3,使用时选装一个。(0的级别最高,包含的转码插件最多,往后越来越少)

stage-0

提供ES7的支持

包含 stage-1stage-2stage-3 的内容

特有的插件

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 支持

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 添加行尾逗号,减少文件的改动

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的asyncawait的支持 及提供幂操作的语法糖

特有插件

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