介绍
Babel 是一个通用的多用途 JavaScript 编译器。通过 Babel 你可以使用(并创建)下一代的 JavaScript,以及下一代的 JavaScript 工具。
作为一种语言,JavaScript 在不断发展,新的标准/提案和新的特性层出不穷。 在得到广泛普及之前,Babel 能够让你提前(甚至数年)使用它们。
Babel 把用最新标准编写的 JavaScript 代码向下编译成可以在今天随处可用的版本。 这一过程叫做“源码到源码”编译, 也被称为转换编译
(transpiling,是一个自造合成词,即转换+编译。以下也简称为转译)。
例如,Babel 能够将新的 ES2015 箭头函数语法:
const square = n => n * n;
转译为:
const square = function square(n) {
return n * n;
};
不过 Babel 的用途并不止于此,它支持语法扩展,能支持像 React 所用的 JSX 语法,同时还支持用于静态类型检查的流式语法(Flow Syntax)。
更重要的是,Babel 的一切都是简单的插件,谁都可以创建自己的插件,利用 Babel 的全部威力去做任何事情。
再进一步,Babel 自身被分解成了数个核心模块,任何人都可以利用它们来创建下一代的 JavaScript 工具。
已经有很多人都这样做了,围绕着 Babel 涌现出了非常大规模和多样化的生态系统。 在这本手册中,我将介绍如何使用 Babel 的内建工具
以及一些来自于社区的非常有用的东西。
Babel 编译代码的几种方式
由于 JavaScript 社区没有统一的构建工具、框架、平台等等,因此 Babel 正式集成了对所有主流工具的支持。 从 Gulp 到 Browserify,
从 Ember 到 Meteor,不管你的环境设置如何,Babel 都有正式的集成支持。
本手册的目的主要是介绍 Babel 内建方式的安装,不过你可以访问交互式的安装页面来查看其它的整合方式。
注意: 本手册将涉及到一些命令行工具如 node 和 npm。在继续阅读之前请确保你已经熟悉这些工具了。
babel-cli
Babel 的 CLI 是一种在命令行下使用 Babel 编译文件的简单方法。
让我们先全局安装它来学习基础知识。
$ npm install --global babel-cli
我们可以这样来编译我们的第一个文件:
$ babel my-file.js
这将把编译后的结果直接输出至终端。使用 --out-file
或着 -o
可以将结果写入到指定的文件。
$ babel example.js --out-file compiled.js
/* 或 */
$ babel example.js -o compiled.js
如果我们想要把一个目录整个编译成一个新的目录,可以使用 --out-dir
或者 -d
。
$ babel src --out-dir lib
/* 或 */
$ babel src -d lib
在项目内运行babel-cli
尽管你可以把 babel-cli
全局安装在你的机器上,但是按项目逐个安装在本地会更好。
- 有两个主要的原因:
- 在同一台机器上的不同项目或许会依赖不同版本的 Babel 并允许你有选择的更新。
- 这意味着你对工作环境没有隐式依赖,这让你的项目有很好的可移植性并且易于安装。
要在(项目)本地安装 Babel CLI 可以运行:
$ npm install --save-dev babel-cli
安装完成后,你的 package.json
应该如下所示:
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
现在,我们不直接从命令行运行 Babel
了,取而代之我们将把运行命令写在 npm scripts 里,这样可以使用 Babel
的本地版本。
只需将 "scripts"
字段添加到你的 package.json
文件内并且把 babel 命令写成 build
字段。
{
"name": "my-project",
"version": "1.0.0",
"scripts": {
"build": "babel src -d lib"
},
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
现在可以在终端里运行:
npm run build
这将以与之前同样的方式运行 Babel,但这一次我们使用的是本地副本。
babel-node
babel-cli工具自带一个babel-node
命令,提供一个支持ES6的REPL环境。
它支持Node的REPL环境的所有功能,而且可以直接运行ES6代码。
它不用单独安装,而是随babel-cli一起安装。然后,执行babel-node
就进入PEPL环境。
$ babel-node
> (x => x * 2)(1)
2
babel-node命令可以直接运行ES6脚本。将上面的代码放入脚本文件es6.js,然后直接运行。
$ babel-node es6.js
2
babel-node也可以安装在项目中。
$ npm install --save-dev babel-cli
然后,改写package.json。
{
"scripts": {
"script-name": "babel-node script.js"
}
}
上面代码中,使用babel-node
替代node
,这样script.js本身就不用做任何转码处理。
babel-register
babel-register
模块改写require命令,为它加上一个钩子。
此后,每当使用require加载.js、.jsx、.es和.es6后缀名的文件,就会先用Babel进行转码。
首先安装 babel-register:
$ npm install --save-dev babel-register
使用时,必须首先加载babel-register。
require("babel-register");
require("./index.js");
然后,就不需要手动对index.js转码了。
**注意: **babel-register只会对require命令加载的文件转码,而不会对当前文件转码。
另外,由于它是实时转码,所以只适合在开发环境使用。
babel-core
如果某些代码需要调用Babel的API进行转码,就要使用babel-core模块。
安装命令如下。
$ npm install babel-core --save
然后,在项目中就可以调用babel-core。
var babel = require('babel-core');
/* 字符串转码 */
babel.transform('code();', options);
/* => { code, map, ast } */
/* 文件转码(异步)*/
babel.transformFile('filename.js', options, function(err, result) {
result; /* => { code, map, ast } */
});
/* 文件转码(同步)*/
babel.transformFileSync('filename.js', options);
/* => { code, map, ast } */
/* Babel AST转码 */
babel.transformFromAst(ast, code, options);
/* => { code, map, ast } */
配置对象options,可以参看官方文档http://babeljs.io/docs/usage/options/。
下面是一个例子。
var es6Code = 'let x = n => n + 1';
var es5Code = require('babel-core')
.transform(es6Code, {
presets: ['es2015']
})
.code;
// '"use strict";\n\nvar x = function x(n) {\n return n + 1;\n};'
上面代码中,transform方法的第一个参数是一个字符串,表示需要转换的ES6代码,第二个参数是转换的配置对象。
babel-loader
我们大部分情况下,做前端项目是有一套自己的构建、打包过程的,这个过程会对js进行压缩等处理。而这种情况下,我们要用ES6,就可以顺便把babel加入到这个构建过程当中(岂不是更加灵活咯)。而babel也为webpack
这类的工具提供了对应的loader。
(loader是webpack里的概念哦,有了babel-loader
,webpack
就能在打包过程中加入babel的强大编译功能了)
其实babel除了能支持webpack,也能支持JavaScript社区所有的主流构建工具,这里就不做说明了。
babel-loader的使用方法实际上跟你使用命令CLI或者API的方式都是一模一样的。只是这个调用者变成了webpack,webpack执行时其实类似于你通过babel API来转译你的源码。
所以他们之间的关系是: webpack依赖babel-loader, babel-loader依赖babel编译相关的包(如babel-core), 而babel编译又依赖自身或社区一些插件(如preset-env等)。
看下使用babel-loader时,webpack的配置文件:
{
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
]
}
]
}
}
由于babel有自己的配置文件,所以上面代码中babel-loader中的options
配置可以不写,而是放到独立配置文件当中。
配置Babel
上面呢我们已经学习了调用babel
的N种方式。可以说,无论哪种方式调用吗,都离不开babel的配置文件的配置(否则babel什么都不做)。
现在我们学习如何配置babel
。Babel的配置文件是.babelrc
或者babel.config.js(babel7推荐的)
,存放在项目的根目录下, rc结尾的文件通常代表运行时自动加载的文件、配置。
使用Babel的第一步,就是配置这个文件。
你可以通过安装**插件(plugins)或预设(presets,也就是一组插件)**来指示 Babel 去做什么事情。
.babelrc
在我们告诉 Babel 该做什么之前,我们需要创建一个配置文件。你需要做的就是在项目的根路径下创建 .babelrc
文件。然后输入以下内容作为开始:
{
"presets": [],
"plugins": []
}
这个文件就是用来让 Babel 做你要它做的事情的配置文件。
**注意:**尽管你也可以用其他方式给 Babel 传递选项,但 .babelrc 文件是约定也是最好的方式。
plugins插件
babel6以后,babel自身只能完成基本的核心功能。并不去做转换任何语法特性的事情。
比如transform-es2015-classes
这个插件就可以让babel转译你代码中的class定义的语法。
比如如果在babel6里想用箭头函数,得装上插件:
npm install babel-plugin-transform-es2015-arrow-functions
然后设置babelrc配置文件:
{
"plugins": ["transform-es2015-arrow-functions"]
}
babel官方内置插件都在babel的官方仓库package目录下(babel-cli代码也在这): 链接
这里就不列举了。
preset预设
但是这么多插件,写起来非常麻烦。总不能让开发者记住所有插件的功能并且去配置上项目所需要的插件吧。这显然不行,所以有了preset预设。一个预设就包含了很多插件咯。
preset预设是一系列plugin插件的集合,配置了该预设,就不需要配置n个插件了,减少了配置的繁琐。
比如使用preset-es2015
的预设为什么就可以转换class定义这种语法呢,其实就因为 es2015的预设中已经包含了transform-es2015-classes
这个插件。
官方的预设还是在babel的这个仓库里
babel内置的预设如下:
- env
- es2015
- es2016
- es2017
- latest (已经废弃,请用preset-env代替)
- react
- flow
还有其他一些非官方的预设,可以在npm上进行搜索: https://www.npmjs.com/search?..
?: babel-preset-stage-x
JavaScript 还有一些提案,正在积极通过 TC39(ECMAScript 标准背后的技术委员会)的流程成为标准的一部分。
这个流程分为 5(0-4)个阶段。 随着提案得到越多的关注就越有可能被标准采纳,于是他们就继续通过各个阶段,最终在阶段 4 被标准正式采纳。
以下是4 个不同阶段的(打包的)预设:
- babel-preset-stage-0
- babel-preset-stage-1
- babel-preset-stage-2
- babel-preset-stage-3
- babel-preset-stage-4
注意 stage-4 预设是不存在的因为它就是上面的 es2015 预设。
以上每种预设都依赖于紧随的后期阶段预设。
例如,babel-preset-stage-1
依赖 babel-preset-stage-2
,后者又依赖 babel-preset-stage-3
。
使用的时候只需要安装你想要的阶段就可以了:
$ npm install --save-dev babel-preset-stage-2
然后添加进你的 .babelrc
配置文件。
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
preset-env预设
有一个预设叫做 babel-preset-env
, 他是一个高级的预设,能编译 ES2015+ 到 ES5,但它是根据你提供的目标浏览器版本和运行时环境来决定使用哪些插件和polyfills。
这个预设是 babel7 里面唯一推荐使用的预设, babel7建议废弃掉其他所有的预设。
preset-env的目标是 make your bundles smaller and your life easier
对于preset-env预设来说,如果不做任何配置:
{
"presets": ["env"]
}
那么preset-env就相当于 babel-preset-latest
这个预设。它包含所有的 babel-preset-es2015, babel-preset-es2016, and babel-preset-es2017 预设。
如果你了解你的目标用户所使用的平台(比如大部分用户都使用了较新的浏览器),那么你大可不必转译所有的特性。你只需要告诉babel让他转译你目标平台现在不支持的语法即可。
此时你需要配置一个数组写法, 且第二个元素是个对象用来配置preset-env的options:
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "ie >= 10"]