一,为了更明白地使用Babel, 先了解Babel 的发展过程. 现在Babel的版本是6, 相对于以前的版本, 它做了重大更新:
1, 模块化:所有的内部组件都变成了单独的包。打开Babel在GitHub上的地址, 我们看到了Packages(包), 它分为了Core Packages (babel-core) 和 Other 如 (babel-cli, babel-polyfill), 这些都是一个一个单独的包.
看一下 核心包 babel-core:
Babel-core is the Babel compiler itself; it exposes the babel.transform
method, where transformedCode = transform(src).code
. babel-core 就是Babel 编译器它自身, 提供了一个方法 babel.transform.
再看一下包 babel-cli: Babel-cli is the CLI tool that runs babel-core
and helps with outputting to a directory, a file, stdout and more. Babel-cli 是命令行工具, 它运行 babel-core, 输出目录、文件等,可以看到它依赖 babel- core.
总结: 每一个内部组件都变成了独立的包,每个包又都定义了一些轻量级的公共API, 可以供其他包使用。
2, 插件化: 这就是GitHub页面中提到的 Presets and Plugins.
Plugins are the heart of Babel and what make it work. 插件是Babel 的核心, 是插件让Babel 正常工作, 主要是Transform Plugins(转换器插件), 就是它把ES6+ 转换成ES5. Presets are just simply an array of plugins. 预设仅仅是插件的集合。
二, 写一些简单的代码,加深理解.
1, 新建一个babel 文件夹, 然后在该文件夹下面 npm init 创建一个package.json 文件, npm init –y 可以快速创建一个package.json 文件。
2, 创建两个文件夹, src(用于存放编译前的文件), lib(用于放编译后的文件)。在src 目录下新建一个index.js 文件用于编写代码, 这里写一个箭头函数如下:
let sum = (num1, num2) => num1 + num2;
console.log(sum(3, 5))
3, 这里只想用命令行工具对代码进行编译. 根据上面提到的模块化思想,如果我们只想使用某些内部组件执行某种构建任务,只需要单独的去安装相应的包就可以了. 这里,我们只想用一下babel-cli 命令行工具, 那么我们只需要安装这个包,npm install --save-dev babel-cli, 安装之后,这里用到一个命令,”babel src –d lib”
4,为了使用这个命令, 我们需要把这个命令放到 npm scripts 中. 打开package.json 文件找到 scripts , 添加 “build”: “babel src –d lib” 以后在 命令行中输入npm run build, 就可以执行编译。
5, 在命令行中输入npm run dev, 我们看到 lib文件夹多了index.js, 但却是原样输出,并没有转换成es5的语法。 这里就用到了它的插件化思想。
The transformer[s] used in Babel are the independent pieces of code that transform specific things. 每个转换器插件也都是彻底独立出来。
After Babel 6, the default transforms were removed; if you don't specify any plugins/presets it will just return the original source code. Babel 6以后, 默认的插件被移除, 如果没有指定一个插件,Babel将后原样输出, 不会进行编译。
也就是说,即时我们安装了Babel, 它也没有用来转换ES5代码的功能, 还需要安装相应的插件。这里,我们用到了箭头函数,所以安装箭头函数插件:npm install --save-dev babel-plugin-transform-es2015-arrow-functions, 为了方便对插件进行管理,Babel 提供了它自己的配置文件 .babelrc, 这时我们在根目录下建立 .babelrc 文件,写入 {"plugins": ["transform-es2015-arrow-functions"] }这时再运行 npm run build, 可以看到,编译成功了。
6, 但是我们看到 let 并没有变化,主要是因为一个插件只做一件事情。如果我们的代码中,大批量的使用es6的特性,那么我们就要列出很多个插件,这非常不方便,所以Babel 提供了插件预设,上面也说了, 预设就是一些插件的集合,这样我们安装预设之后,就是安装了一堆插件,再也不会一个一个安装插件。 npm install --save-dev babel-preset-es2015,通过名字可以看出,这是es2015预设,只要是es2015转换成es5 用到的插件,它都会用装上,这是要在 .babelrc 文件下写 {"presets": ["es2015"] }
presets 不要忘记s. 这时,彻底转化成es5.
三, 预设Preset
看到官方预设preset, 有两种,一个是按年份(babel-preset-2017),一个是按阶段(babel-preset-stage-0)。 这主要是根据tc39 委员会ECMASCRPIT 发布流程来制定的。
1, 按年份:TC39 委员会决定,从2016年开始,每年都会发布一个版本,它包括每年期限内完成的所有功能,同时ECMAScript的版本号也按年份编制,就有了ES2016, ES2017. 所有也就有了babel-present-2016, babel-preset-2017, 对每一年新增的语法进行转化。babel-preset-latest 就是把所有es2015, es2016, es2017 全部包含在一起了。
2, 按阶段: 对于ECMAScript 的功能,每一个提案都会经过5个阶段:
- stage-0 - Strawman: just an idea, possible Babel plugin. (稻草人阶段, 就是一个想法)
- stage-1 - Proposal: this is worth working on. (建议阶段,值得去努力)
- stage-2 - Draft: initial spec. ( 草案阶段, 初始的细节描述)
- stage-3 - Candidate: complete spec and initial browser implementations. (候选阶段,草案基本完成,浏览器厂商实验性的实现)
- stage-4 - Finished: will be added to the next yearly release. (完成阶段,添加到下一年的版本中)
Babel 对应不同的阶段, 提供了不同的预设,babel-preset-stage-0, babel-preset-stage-1, babel-preset-stage-2, babel-preset-stage-3. 这里没有stage-4, 是因为它将添加到下一年的版本中,也就是到了按年份进行预设。所以我们还可以得出其他的几个结论:
1, 按年份进行的预设,其实是tc39委员会已经批准的,浏览器将要实现的功能,这也对应了官网上的 Each yearly preset only compiles what was ratified in that year(年份预设只编译那一年批准的功能).
2, stage-X 的预设,实现是没有被批准的功能。Any transforms in stage-x presets are changes to the language that haven’t been approved to be part of a release of Javascript (such as ES6/ES2015). “Changes to the language are developed by way of a process which provides guidelines for evolving an addition from an idea to a fully specified feature”
3, stage-0 阶段的预设包含的插件大于stage-1阶段包含的插件, stage-1 > stage-2, stage-2 > stage-3, 所以我们安装stage-X预设时,只选装一个就可以了。
4, 如果没有提供es2015 相关的预设,preset-stage-X 这种阶段性的预设也不能用。
四: 总结
明白了Babel,使用起来就比较简单了。
1, 先安装Babel, 就是安装babel-core 这个核心包。如果用 webpack, 我们会看到npm install babel-loader babel-core --save--dev;如果用gulp, 安装gulp-babel插件,其实都是安装的babel-core 这个核心。
2,选择编译的插件或预设,如果只是在编译几个功能,可以选择插件,如果是大批量的安装插件,那就不如安装预设, npm install babel-preset-2015 --save-dev
3, 安装好的插件或预设, 就需要在.babelrc 配置文件中进行配置,如果安装插件,就在 plugins 列出,如是安装预设,就在 presets 列出。也可以同时安装插件与预设,预设也可以同时安装多个,安装state-X之前,必须先安装es2015 等。
五:其实Babel 只是转换了ES6+的语法到ES5, 新增的对象如Promise 和 API 如Array.find() 方法,它并没有转换。我们写一个Array.find()应用,npm run build, 我们发现它是原样输出。那么这时候就需要用到了babel-polyfill了。
简单了解了一个polyfill, 它就是 Replicate(复写) an API using JavaScript (or Flash or whatever) if the browser doesn’t have it natively”。如果浏览器不能原生的支持,我们就把这个API 用JS 重新写出来,那么旧的浏览器就可以使用这个API了。
百度了一下有一个形象的解释:polyfill来自于一个家装产品Polyfilla: Polyfilla是一个英国产品,在美国称之为Spackling Paste(译者注:刮墙的,在中国称为腻子).记住这一点就行:把旧的浏览器想象成为一面有了裂缝的墙.这些[polyfills]会帮助我们把这面墙的裂缝抹平,还我们一个更好的光滑的墙壁(浏览器)。下面的例子是firefox MDN上的,用一个array.find() 方法。
function isBigEnough(element) { return element >= 15; } var ok = [12, 5, 8, 130, 44].find(isBigEnough); console.log(ok);
在chrome 控制台下,可以看到输出130, 但在IE10 下报错了,也就是说IE10不支持这个API, 那么需要用js重新写一个这个api, 下面的代码可以清楚地看到我们用现有的方法把这个API实现了,那么下面这段代码就是一个polyfill.
if (!Array.prototype.find) { Object.defineProperty(Array.prototype, 'find', { value: function(predicate) { // 1. Let O be ? ToObject(this value). if (this == null) { throw new TypeError('"this" is null or not defined'); } var o = Object(this); // 2. Let len be ? ToLength(? Get(O, "length")). var len = o.length >>> 0; // 3. If IsCallable(predicate) is false, throw a TypeError exception. if (typeof predicate !== 'function') { throw new TypeError('predicate must be a function'); } // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. var thisArg = arguments[1]; // 5. Let k be 0. var k = 0; // 6. Repeat, while k < len while (k < len) { // a. Let Pk be ! ToString(k). // b. Let kValue be ? Get(O, Pk). // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). // d. If testResult is true, return kValue. var kValue = o[k]; if (predicate.call(thisArg, kValue, k, o)) { return kValue; } // e. Increase k by 1. k++; } // 7. Return undefined. return undefined; } }); }
把这段代码放到我们的程序中,可以看到IE10输出130.
babel-polyfill 就是把所有的上面这种polyfill 放到一起了。我们可以在浏览器中直接使用。npm install babel-polyfill --save--dev, 安装完成后,node_modules/babel-polyfill/dist 下面有polyfill.min.js, 我们直接拿出这个文件,用script标签引进到我们的
html 文件中,就可以了。
<script src="../node_modules/babel-polyfill/dist/polyfill.min.js"></script> <script src="../lib/index.js"></script>
至此,Babel的基本使用就介绍完了。