Babel 入门教程

作者: 阮一峰

日期: 2016年1月25日

(说明:本文选自我的新书《ES6 标准入门(第二版)》的第一章《ECMAScript 6简介》

Babel是一个广泛使用的转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。

这意味着,你可以现在就用 ES6 编写程序,而不用担心现有环境是否支持。下面是一个例子。


// 转码前
input.map(item => item + 1);

// 转码后
input.map(function (item) {
  return item + 1;
});

上面的原始代码用了箭头函数,这个特性还没有得到广泛支持,Babel将其转为普通函数,就能在现有的JavaScript环境执行了。

一、配置文件.babelrc

Babel的配置文件是.babelrc,存放在项目的根目录下。使用Babel的第一步,就是配置这个文件。

该文件用来设置转码规则和插件,基本格式如下。


{
  "presets": [],
  "plugins": []
}

presets字段设定转码规则,官方提供以下的规则集,你可以根据需要安装。


# ES2015转码规则
$ npm install --save-dev babel-preset-es2015

# react转码规则
$ npm install --save-dev babel-preset-react

# ES7不同阶段语法提案的转码规则(共有4个阶段),选装一个
$ npm install --save-dev babel-preset-stage-0
$ npm install --save-dev babel-preset-stage-1
$ npm install --save-dev babel-preset-stage-2
$ npm install --save-dev babel-preset-stage-3

然后,将这些规则加入.babelrc


  {
    "presets": [
      "es2015",
      "react",
      "stage-2"
    ],
    "plugins": []
  }

注意,以下所有Babel工具和模块的使用,都必须先写好.babelrc

二、命令行转码babel-cli

Babel提供babel-cli工具,用于命令行转码。

它的安装命令如下。


$ npm install --global babel-cli

基本用法如下。


# 转码结果输出到标准输出
$ babel example.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

# -s 参数生成source map文件
$ babel src -d lib -s

上面代码是在全局环境下,进行Babel转码。这意味着,如果项目要运行,全局环境必须有Babel,也就是说项目产生了对环境的依赖。另一方面,这样做也无法支持不同项目使用不同版本的Babel。

一个解决办法是将babel-cli安装在项目之中。


# 安装
$ npm install --save-dev babel-cli

然后,改写package.json


{
  // ...
  "devDependencies": {
    "babel-cli": "^6.0.0"
  },
  "scripts": {
    "build": "babel src -d lib"
  },
}

转码的时候,就执行下面的命令。


$ npm run build

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


$ 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-polyfill

Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。

举例来说,ES6在Array对象上新增了Array.from方法。Babel就不会转码这个方法。如果想让这个方法运行,必须使用babel-polyfill,为当前环境提供一个垫片。

安装命令如下。


$ npm install --save babel-polyfill

然后,在脚本头部,加入如下一行代码。


import 'babel-polyfill';
// 或者
require('babel-polyfill');

Babel默认不转码的API非常多,详细清单可以查看babel-plugin-transform-runtime模块的definitions.js文件。

七、浏览器环境

Babel也可以用于浏览器环境。但是,从Babel 6.0开始,不再直接提供浏览器版本,而是要用构建工具构建出来。如果你没有或不想使用构建工具,可以通过安装5.x版本的babel-core模块获取。


$ npm install babel-core@old

运行上面的命令以后,就可以在当前目录的node_modules/babel-core/子目录里面,找到babel的浏览器版本browser.js(未精简)和browser.min.js(已精简)。

然后,将下面的代码插入网页。


<script src="node_modules/babel-core/browser.js"></script>
<script type="text/babel">
// Your ES6 code
</script>

上面代码中,browser.js是Babel提供的转换器脚本,可以在浏览器运行。用户的ES6脚本放在script标签之中,但是要注明type="text/babel"

另一种方法是使用babel-standalone模块提供的浏览器版本,将其插入网页。


<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.4.4/babel.min.js"></script>
<script type="text/babel">
// Your ES6 code
</script>

注意,网页中实时将ES6代码转为ES5,对性能会有影响。生产环境需要加载已经转码完成的脚本。

下面是如何将代码打包成浏览器可以使用的脚本,以Babel配合Browserify为例。首先,安装babelify模块。


$ npm install --save-dev babelify babel-preset-es2015

然后,再用命令行转换ES6脚本。


$  browserify script.js -o bundle.js \
  -t [ babelify --presets [ es2015 react ] ]

上面代码将ES6脚本script.js,转为bundle.js,浏览器直接加载后者就可以了。

package.json设置下面的代码,就不用每次命令行都输入参数了。


{
  "browserify": {
    "transform": [["babelify", { "presets": ["es2015"] }]]
  }
}

八、在线转换

Babel提供一个REPL在线编译器,可以在线将ES6代码转为ES5代码。转换后的代码,可以直接作为ES5代码插入网页运行。

九、与其他工具的配合

许多工具需要Babel进行前置转码,这里举两个例子:ESLint和Mocha。

ESLint 用于静态检查代码的语法和风格,安装命令如下。


$ npm install --save-dev eslint babel-eslint

然后,在项目根目录下,新建一个配置文件.eslint,在其中加入parser字段。


{
  "parser": "babel-eslint",
  "rules": {
    ...
  }
}

再在package.json之中,加入相应的scripts脚本。


  {
    "name": "my-module",
    "scripts": {
      "lint": "eslint my-files.js"
    },
    "devDependencies": {
      "babel-eslint": "...",
      "eslint": "..."
    }
  }

Mocha 则是一个测试框架,如果需要执行使用ES6语法的测试脚本,可以修改package.jsonscripts.test


"scripts": {
  "test": "mocha --ui qunit --compilers js:babel-core/register"
}

上面命令中,--compilers参数指定脚本的转码器,规定后缀名为js的文件,都需要使用babel-core/register先转码。

(完)

留言(64条)

通俗易懂。谢谢!

gulp-babel走起

对于 export default {} 支持不好,还得加个插件 babel-plugin-add-module-exports:
"plugins": [
"add-module-exports"
]

引用郁也风的发言:

对于 export default {} 支持不好,还得加个插件 babel-plugin-add-module-exports:
"plugins": [
"add-module-exports"
]

早看到这条评论就好了

很清晰,很有帮助。刚好这几天在折腾babel6。支持一下~

能否讲解一下配置文件.babelrc,这是在Linux环境的配置文件吧,在Windows环境下如何搭建一个项目,能详细讲述么?

多谢老师指导, 拜读。

很有帮助!

在使用babel-polyfill后,需要用bind方法绑定this的时候有所改变,this前面需要增加一个参数 如:obj.onclick(function(){console.log(this)}.bind(null, this))

学到不少

阮老师,babel-core里最后示例中es5Code和es6Code这两个变量名称是不是起反了.

@marsoln:

谢谢指出,已经改正。

babel-polyfill 包我看了看还是挺大的,生产环境如何替换啊??

引用wy的发言:

能否讲解一下配置文件.babelrc,这是在Linux环境的配置文件吧,在Windows环境下如何搭建一个项目,能详细讲述么?

.babelrc. 在后邊再加上一點

正在接触ES6语法 学习了

刚开始接触ES6语法 学习了

babel 编译import 会编译成require 还是运行不了 请教下 怎么解决

引用Ben Suen的发言:

.babelrc. 在后邊再加上一點

试了下,并不行

想问一下.bablrc是文件名还是扩展名,如果是扩展名,那么文件名是什么,好怪的写法。

babel5升级babel6带来的export default问题真是烦

谢谢您通俗易懂的小教程。

没有网络的环境下离线怎么安装babel

如果我在babelrc中加入了 `babel-plugin-transform-runtime` 可以不选择 `babel-polyfill` 吗?
相关配置如下:
{
"presets": ["latest", "react", "stage-0"],
"plugins": ["transform-runtime"]
}

引用leo的发言:

如果我在babelrc中加入了 `babel-plugin-transform-runtime` 可以不选择 `babel-polyfill` 吗?
相关配置如下:
{
"presets": ["latest", "react", "stage-0"],
"plugins": ["transform-runtime"]
}

babel-polyfill 与 babel-runtime 是两个概念

babel-polyfill 是对浏览器缺失API的支持。比如浏览器可能没有Array.from() 方法。

babel-runtime 是为了减少重复代码而生的。 babel生成的代码,可能会用到一些_extend(), classCallCheck() 之类的工具函数,默认情况下,这些工具函数的代码会包含在编译后的文件中。如果存在多个文件,那每个文件都有可能含有一份重复的代码。

babel-runtime插件能够将这些工具函数的代码转换成require语句,指向为对babel-runtime的引用,如 require('babel-runtime/helpers/classCallCheck'). 这样, classCallCheck的代码就不需要在每个文件中都存在了。

当然,最终你需要利用webpack之类的打包工具,将runtime代码打包到目标文件中。

仔细阅读了下文档, 我上面说的是有问题的。

确实,用了babel-runtime 就可以不用 babel-polyfill 了,参见官方文档:

http://babeljs.io/docs/plugins/transform-runtime/


请问阮一峰老师,在html里引入后,再用script标签引入外部脚本,浏览器就会报错跨域,这是怎么回事?

挺不错的。

看了很多阮老师的文章,都讲的很细很全面,赞!

请问一下,老师,我在看您书的第2张第2.3节的const命令使用时,您书中是这样的描述的:

“const的作用域与let命令相同,只在所声明所在的块级作用域内有效”

代码:

if(true){
//console.log(MAX); // 在浏览器中测试时,与你的一致,但是在nodejs是undefined
const MAX = 5;
// console.log(MAX); // 5 在chrome浏览器下您的说法是正确的
}

console.log(MAX); // Uncaught ReferenceError: MAX is not defined,这个也证实了您的结论,只有在所在的块级作用域内有效

但是我把这段代码放在nodejs环境中,这段代码是能通过的,用babel-node 源js文件,却输出了5,正常运行,const 声明的变量只针对块级作用域有效,但这是不是矛盾了呢?不是不会变量的提升?还有const和let,在声明前调用就会报错,但是在nodejs环境中,它却是undefined,报错与undefined并不等同吧.

我猜想了一下:提出如下可能:

一:nodejs的环境原因所致,我用es-checker测了一下自己的nodejs对es6的支持程度,比书中的要高69%;您也说了,在2016会小部分的改动。

二:本身我自身的chrome浏览器配置的原因,chrome版本原因。。

网上查了一下,我并没有找到答案,还希望老师能够解下我的凝惑的,谢谢

能解释下babel-pollyfill 与babel-runtime的作用吗,不太清楚这两则是干什么用的

想问一下阮大牛。。现在用的typescript,然后ts这一套已经比较熟练了,bable是不是只需要了解下

引用farmerZ的发言:

想问一下.bablrc是文件名还是扩展名,如果是扩展名,那么文件名是什么,好怪的写法。

rc结尾的文件通常代表运行时自动加载的文件,配置等等的

我用的nodejs6最新版,.babelrc文件的内容是
{
"presets": [
["env", { "modules": false }],
"stage-2"
],
"plugins": ["transform-runtime"],
"comments": false,
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": [ "istanbul" ]
}
}
}
通用版,
不是通常的
{"perset":["es-2015","stage-2"],
"plugins":["transfrom-runtime"],
"comments":false
}

是不是不用配置这些环境变量,直接用通用版也可以运行es6了

不吝赐教


babel-node也可以安装在项目中。

$ npm install --save-dev babel-cli

应该是想敲

$ npm install --save-dev babel-node

当js中使用了import和export时
使用babel-node xxx.js还是报错不支持import和export,有什么办法解决么。。本来装babel-cli就是因为nodejs不支持import和export,装了babel-cli还是不支持就白装了。。

引用moonlightv的发言:

当js中使用了import和export时
使用babel-node xxx.js还是报错不支持import和export,有什么办法解决么。。本来装babel-cli就是因为nodejs不支持import和export,装了babel-cli还是不支持就白装了。。

我知道可以用webpack2避免这个问题,不知道还有没有其他办法。

引用moonlightv的发言:

当js中使用了import和export时
使用babel-node xxx.js还是报错不支持import和export,有什么办法解决么。。本来装babel-cli就是因为nodejs不支持import和export,装了babel-cli还是不支持就白装了。。

引用moonlightv的发言:

当js中使用了import和export时
使用babel-node xxx.js还是报错不支持import和export,有什么办法解决么。。本来装babel-cli就是因为nodejs不支持import和export,装了babel-cli还是不支持就白装了。。

请问 你是怎么解决的 如果不用 webpack

阮哥的博绝对可以出教科书啦〜

想请问一下,安装了babel-cli但是执行babel-node的命令总是出错:
Cannot find module 'C:\Users\cjh\AppData\Roaming\npm\node_modules\.babel-cli_npminstall\node_modules\.6.18.0@babel-cli\bin\babel-node.js'
at Function.Module._resolveFilename (module.js:469:15)
at Function.Module._load (module.js:417:25)
at Module.runMain (module.js:604:10)
at run (bootstrap_node.js:393:7)
at startup (bootstrap_node.js:150:9)
at bootstrap_node.js:508:3
这是什么情况

阮老师写的文章一如既往的通俗易懂,太感谢了

.babelrc文件中
"plugins": ["transform-runtime", [
"component", [{
"libraryName": "element-ui",
"styleLibraryName": "theme-default"
}]
]],
我看同事下载完element-ui后,添加的,这是什么意思

npm install babel-core@5 为什么现在 安装不了啊! 直接

npm ERR! code E404
npm ERR! 404 Not Found: bable-core@latest

npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\Administrator\AppData\Roaming\npm-cache\_logs\2017-08-05T10_14_55_458Z-debug.log

@冷寒

刚刚发现,现在要用 npm install babel-core@old

报错Requiring external module babel-register,使用了命令 $npm install --save-dev babel-register还是不行,求教~

老师我用babel进行转码是出现这种let f = f=>a+b;var f = function f(){
return a+b;}这是为啥?为啥f多了一次

请问大神 为什么我在运行gulp时 出现了我发的错误啊
那个babel-core 还有babel-register我都安装了
还是有错误

Requiring external module babel-register

引用brookslee的发言:

仔细阅读了下文档, 我上面说的是有问题的。

确实,用了babel-runtime 就可以不用 babel-polyfill 了,参见官方文档:

http://babeljs.io/docs/plugins/transform-runtime/


Another purpose of this transformer is to create a sandboxed environment for your code. If you use babel-polyfill and the built-ins it provides such as Promise, Set and Map, those will pollute the global scope. While this might be ok for an app or a command line tool, it becomes a problem if your code is a library which you intend to publish for others to use or if you can’t exactly control the environment in which your code will run.

那里说了用runtime 就不必 polyfill了

对于新手来说。完全看不懂在说什么。配置全都跳过了。学个p

非入门者可以看懂!!!

今天来按照阮老师的文章npm run bulid 并不行,一直提示我can't find module
我已经安装了很多,还是不行,觉得哪儿出了问题

阮大神更新下env了。

有个问题一直很疑惑:
babel-eslint在处理带有flowtype的文件时,究竟是eslint自己解析了flow,还是靠babel先把flow去掉?
如果是后者,那eslint应该看不到半点类型信息,就不可能对react component里的props/states做验证了,且与事实不符(vscode里eslint验证的很好);
如果是前者,那babel和eslint是啥子关系?

老师, 我这里使用了no-unused-vars, 但是有的引入中, 使用的地方在注解(例:@xxx), 或者是typescript的属性中(例: IMap), 这种情况下就会提示, 引入的这个xxx注解或者自定义的IMap属性类型没有被使用到, 应该怎么处理呢?

创建 .babelrc文件的时候,此文件不需要文件名,直接 .babelrc 即可,否则配置文件中的配置无效,我卡在这个地方一天。。。。。

看了这么多教程,还是阮老师靠谱

Symbol 经过转码之后是啥样的呢

知道what is the different webpack ast and babel ast吗,知道的话,欢迎交流,

阮老师,为什么命令行输入babel-node总是提示没有此命令(command not found),在此之前的练习结果都跟老师对的上

emmm,如果后端nodejs想同时接入babel和ts有啥好的解决方案么。现在卡在babel-node无法识别ts的类型标注。

请教一下,我用Vue-element-admin做了两个工程(一个用户APP用,一个管理后台),有一总体共用的代码(比如:公用的数据,filter和一些函数库)我打算用一个npm模块通过npm link的方式共享给两个工程使用。
一翻修改之后一切正常,但在mock中通过mock-server加载的文件,不支持按ES6的import和export default方式导出,请问为什么在同一个工程中有些加载的代码支持,而有些又不支持呢,有解决方法吗?

阮老师,评论可弄一个支持markdown语法的功能,方便贴代码。

babel-node也可以安装在项目中。
下面的例子写成babel-cli了。。。。

引用Emma的发言:

阮老师,为什么命令行输入babel-node总是提示没有此命令(command not found),在此之前的练习结果都跟老师对的上

可以 npx babel-node

我要发表看法

«-必填

«-必填,不公开

«-我信任你,不会填写广告链接