Babel 把用最新标准编写的 JavaScript 代码向下编译成能够在今天随处可用的版本。 这一过程叫作“源码到源码”编译, 也被称为转换编译。javascript
15 年 11 月,Babel 发布了 6.0 版本。相较于前一代 Babel 5,新一代 Babel 更加模块化, 将全部的转码功能以插件的形式分离出去,默认只提供 babel-core。本来只须要装一个 babel ,如今必须按照本身的需求配置,灵活性提升的同时也提升了使用者的学习成本。html
npm i babel
已经弃用,你能下载到的仅仅是一段 console.warn,告诉你 babel 6 再也不以大杂烩的形式提供转码功能了。java
例如,Babel 可以将新的 ES2015 箭头函数语法:node
let fun = () => console.log('babel')
转译为:react
"use strict"; var fun = function fun() { return console.log('babel'); };
不过 Babel 的用途并不止于此,它支持语法扩展,能支持像 React 所用的 JSX 语法,更重要的是,Babel 的一切都是简单的插件,谁均可以建立本身的插件,利用 Babel 的所有威力去作任何事情。
再进一步,Babel 自身被分解成了数个核心模块,任何人均可以利用它们来建立下一代的 JavaScript 工具。webpack
babel-cli
Babel 的 CLI 是一种在命令行下使用 Babel 编译文件的简单方法。git
让咱们先全局安装它来学习基础知识。es6
$ npm install --global babel-cli
咱们能够这样来编译咱们的第一个文件:github
$ babel my-file.js
这将把编译后的结果直接输出至终端。使用 --out-file 或着 -o 能够将结果写入到指定的文件。web
$ 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-core
若是你须要以编程的方式来使用 Babel,可使用 babel-core 这个包。
babel-core 的做用是把 js 代码分析成 ast ,方便各个插件分析语法进行相应的处理。有些新语法在低版本 js 中是不存在的,如箭头函数,rest 参数,函数默认值等,这种语言层面的不兼容只能经过将代码转为 ast,分析其语法后再转为低版本 js。
首先安装 babel-core。.
$ npm install babel-core
var babel = require("babel-core");
字符串形式的 JavaScript 代码能够直接使用 babel.transform 来编译。
babel.transform("code();", options); // => { code, map, ast }
若是是文件的话,可使用异步 api:
babel.transformFile("filename.js", options, function(err, result) { result; // => { code, map, ast } });
或者是同步 api:
babel.transformFileSync("filename.js", options); // => { code, map, ast }
还能够经过babel-register
和babel-node
使用Babel,但因为这两种用法不适合生产环境故省略。
你或许已经注意到了,目前为止经过运行 Babel 本身咱们并没能“翻译”代码,而仅仅是把代码从一处拷贝到了另外一处。缘由就是从Babel 6之后, 默认的插件被移除, 若是没有指定一个插件,Babel将会原样输出, 不会进行编译。
你能够经过安装插件(plugins)或预设(presets,也就是一组插件)来指示 Babel 去作什么事情。
插件只是单一的功能,例如
es2015-arrow-functions
es2015-classes
es2015-for-of
es2015-spread
如下是安装箭头函数的插件方式
npm install --save-dev babel-plugin-transform-es2015-arrow-functions
若是咱们一个一个引人功能单一的插件的话显得特别麻烦,一般咱们用的更多的是预设。插件和预设一般写入到配置文件中。能够将配置写入package.json
的‘babel’属性里,或者是一个单独的.babelrc
文件。
.babelrc
在咱们告诉 Babel 该作什么以前,你须要作的就是在项目的根路径下建立 .babelrc
文件。而后输入如下内容做为开始:
{ "presets": [], "plugins": [] }
这个文件就是用来让 Babel 作你要它作的事情的配置文件。
babel-preset-es2015
预设 babel-preset 系列打包了一组插件,相似于餐厅的套餐。如 babel-preset-es2015
打包了 es6 的特性,babel-preset-stage-0
打包处于 strawman 阶段的语法
咱们须要安装 "es2015" Babel 预设:
$ npm install --save-dev babel-preset-es2015
咱们修改 .babelrc
来包含这个预设。
{ "presets": [ + "es2015" ], "plugins": [] }
一样的,还有babel-preset-2016
,babel-preset-2017
。
babel-preset-latest
latest是一个特殊的presets,包括了es2015,es2016,es2017的插件(目前为止,之后有es2018也会包括进去)。即老是包含最新的编译插件。
babel-preset-env
上面提到的各类preset的问题就是: 它们都太”重”了, 即包含了过多在某些状况下不须要的功能. 好比, 现代的浏览器大多支持ES6的generator, 可是若是你使用babel-preset-es2015, 它会将generator函数编译为复杂的ES5代码, 这是没有必要的。但使用babel-preset-env, 咱们能够声明环境, 而后该preset就会只编译包含咱们所声明环境缺乏的特性的代码,所以也是比较推荐的方式。
安装babel-preset-env
npm install babel-preset-env --save-dev
添加配置
{ "presets": ["env"] }
当没有添加任何的配置选项时,babel-preset-env
默认行为是和babel-preset-latest
是同样的。
下面咱们经过一些例子来看babel-preset-env
的配置是如何使用的:
指定支持主流浏览器最新的两个版本以及IE 7+:
"presets": [ [ "env", { "targets": { "browsers": ["last 2 versions", "ie >= 7"] } } ] ] }
支持超过市场份额5%的浏览器:
"targets": { "browsers": "> 5%" }
某个固定版本的浏览器:
"targets": { "chrome": 56 }
更多的配置请查看官方文档
babel-preset-stage-x
官方预设(preset), 有两种,一个是按年份(babel-preset-2017),一个是按阶段(babel-preset-stage-0)。 这主要是根据TC39 委员会ECMASCRPIT 发布流程来制定的。TC39 委员会决定,从2016年开始,每一年都会发布一个版本,它包括每一年期限内完成的全部功能,同时ECMAScript的版本号也按年份编制,就有了ES2016, ES2017。因此也就有了babel-present-2016, babel-preset-2017, 对每年新增的语法进行转化。babel-preset-latest 就是把全部es2015, es2016, es2017 所有包含在一块儿了。
最终在阶段 4 被标准正式采纳。
如下是4 个不一样阶段的(打包的)预设:
babel-preset-stage-0
babel-preset-stage-1
babel-preset-stage-2
babel-preset-stage-3
注意 stage-4 预设是不存在的由于它就是上面的
es2017
预设。
以上每种预设都依赖于紧随的后期阶段预设,数字越小,阶段越靠后,存在依赖关系。也就是说stage-0是包括stage-1的,以此类推。也就是说这些stage包含的特性是比latest更新的特性但还未被写入标准进行发布。
使用的时候只须要安装你想要的阶段就能够了:
$ npm install --save-dev babel-preset-stage-2
而后添加进你的 .babelrc
配置文件。可是要注意若是没有提供es2017相关的预设,preset-stage-X 这种阶段性的预设也不能用。
Babel 几乎能够编译全部时新的 JavaScript 语法,但对于 APIs 来讲却并不是如此。例如: Promise、Set、Map 等新增对象,Object.assign、Object.entries等静态方法。
为了达成使用这些新API的目的,社区又有2个实现流派:babel-polyfill和babel-runtime+babel-plugin-transform-runtime。
这两个模块功能几乎相同,就是转码新增 api,模拟 es6 环境,但实现方法彻底不一样。babel-polyfill 的作法是将全局对象统统污染一遍,好比想在 node 0.10 上用 Promise,调用 babel-polyfill 就会往 global 对象挂上 Promise 对象。对于普通的业务代码没有关系,但若是用在模块上就有问题了,会把模块使用者的环境污染掉。
babel-runtime 的作法是本身手动引入 helper 函数,仍是上面的例子,const Promise = require('babel-runtime/core-js/promise') 就能够引入 Promise。
但 babel-runtime 也有问题,第一,很不方便,第二,在代码中中直接引入 helper 函数,意味着不能共享,形成最终打包出来的文件里有不少重复的 helper 代码。因此,babel 又开发了 babel-plugin-transform-runtime,这个模块会将咱们的代码重写,如将 Promise 重写成 _Promise(只是打比方),而后引入_Promise helper 函数。这样就避免了重复打包代码和手动引入模块的痛苦。
babel-polyfill
为了解决这个问题,咱们使用一种叫作 Polyfill(代码填充,也可译做兼容性补丁) 的技术。 简单地说,polyfill便是在当前运行环境中用来复制(意指模拟性的复制,而不是拷贝)尚不存在的原生 api 的代码。能让你提早使用还不可用的 APIs,Array.from
就是一个例子。
Babel 用了优秀的 core-js 用做 polyfill,而且还有定制化的 regenerator 来让 generators(生成器)和 async functions(异步函数)正常工做。
要使用 Babel polyfill,首先用 npm 安装它:
$ npm install --save babel-polyfill
而后只须要在文件顶部导入 polyfill 就能够了:
import "babel-polyfill";
babel-runtime
与 babel-polyfill 同样,babel-runtime 的做用也是模拟 ES2015 环境。只不过,babel-polyfill 是针对全局环境的,引入它,咱们的浏览器就好像具有了规范里定义的完整的特性 – 虽然原生并未实现。
babel-runtime 更像是分散的 polyfill 模块,咱们能够在本身的模块里单独引入,好比 require(‘babel-runtime/core-js/promise’) ,它们不会在全局环境添加未实现的方法,只是,这样手动引用每一个 polyfill 会很是低效。咱们借助 Runtime transform 插件来自动化处理这一切。
经过安装 babel-plugin-transform-runtime
和 babel-runtime
来开始。
$ npm install --save-dev babel-plugin-transform-runtime $ npm install --save babel-runtime
而后更新 .babelrc
:
{ "plugins": [ "transform-runtime", "transform-es2015-classes" ] }
如今,Babel 会把这样的代码:
class Foo { method() {} }
编译成:
import _classCallCheck from "babel-runtime/helpers/classCallCheck"; import _createClass from "babel-runtime/helpers/createClass"; let Foo = function () { function Foo() { _classCallCheck(this, Foo); } _createClass(Foo, [{ key: "method", value: function method() {} }]); return Foo; }();
这样就不须要把 _classCallCheck
和 _createClass
这两个助手方法放进每个须要的文件里去了。
那何时用 babel-polyfill
何时用 babel-runtime
呢?若是你不介意污染全局变量(如上面提到的业务代码),放心大胆地用 babel-polyfill
;而若是你在写模块,为了不污染使用者的环境,没的选,只能用 babel-runtime
+ babel-plugin-transform-runtime
。
options
不少预设和插件都有选项用于配置他们自身的行为。 例如,不少转换器都有“宽松”模式,经过放弃一些标准中的行为来生成更简化且性能更好的代码。
要为插件添加选项,只须要作出如下更改:
{ "plugins": [ "transform-runtime", - "transform-es2015-classes", + ["transform-es2015-classes", { "loose": true }] ] }
plugins/presets排序:
具体而言,plugins优先于presets进行编译。
plugins按照数组的index增序(从数组第一个到最后一个)进行编译。
presets按照数组的index倒序(从数组最后一个到第一个)进行编译。由于做者认为大部分会把presets写成["es2015", "stage-0"]。具体细节能够看这个。
不多有大型项目仅仅须要 babel,通常都是 babel 配合着 webpack 或 glup 等编译工具一块儿上的。
为了显出 babel 的能耐,咱们分别配个用 babel-polyfill
和 babel-runtime
、支持 react 的webpack.config.js
先来配使用 babel-runtime
的:
首先安装:
npm install babel-loader babel-core babel-preset-es2015 babel-plugin-transform-runtime webpack --save-dev
npm install babel-runtime --save
而后配置
module: { loaders: [{ loader: 'babel', test: /\.jsx?$/, include: path.join(__dirname, 'src'), query: { plugins: ['transform-runtime'], presets: [ ["env", { "targets": { "chrome": 52 }, "modules": false, "loose": true }], 'stage-2', 'react' ], } }] }
须要注意的是,babel-runtime
虽然没有出如今配置里,但仍然须要安装,由于 transform-runtime
依赖它。
再来个 babel-polyfill
的:
entry: [ 'babel-polyfill', 'src/index.jsx', ], module: { loaders: [{ loader: 'babel', test: /\.jsx?$/, include: path.join(__dirname, 'src'), query: { presets: [ ["env", { "targets": { "chrome": 52 }, "modules": false, "loose": true }], 'stage-2', 'react', ], } }] }
参考文档:
http://babeljs.io/
https://github.com/thejamesky...
https://excaliburhan.com/post...
https://icyfish.me/2017/05/18...