Babel 的使用
可以说,有什么样的需求就催生了什么样的工具,Babel 就是这样一个工具,作为一个被广泛使用的 ES6 转换器,它能够将 ES6 代码转为 ES5 代码,从而在现有环境执行。这意味着,你现在就可以开始使用 ES6 的方式来编写代码,而不用担心现有环境是否完全支持。
var array = [1,2,3,4];
// 转码前
array.map(item => item + 1);
// 转码后
array.map(function (item) {
return item + 1;
});
上面的代码使用了箭头函数, Babel 会将它其转换为普通函数。
Babel 官方网站
配置文件
首先我们安装 babel-cli ,这个是 babel 的官方命令行工具,允许我们直接利用 babel 的命令来编译js文件,之后我还将阐述如何利用 webpack 来打包代码并转换成 es5 的规格,但是在这之前,我们最好还是先学习如何使用 babel
由于我们不会经常使用 babel 的全局命令,所以我们要执行本地安装
还记得我们之前的那个 my-project 吗,我们在里面本地安装 babel-cli
npm install babel-cli --save-dev
你的 package.json 出现:
"devDependencies": {
"babel-cli": "^6.0.0"
}
然后我们在 scripts 中加上:
"scripts": {
"build": "babel index.js --out-file index-compiled.js"
}
这样我们就可以利用 babel 将 index.js 转换为 index-compiled.js 文件
要使用 Babel 我们首先要为它建立一个配置文件 .babelrc ,存放在根目录下。使用Babel 的第一步就是配置这个文件,这个文件用来设置转码和规则,基本格式如下:
{
"presets": [],
"plugins": []
}
presets 预设
presets(预设集) 字段设置转码规则,官方提供了以下规则集
- env
- es2015
- es2016
- es2017
- latest (deprecated in favor of env)
- react
- flow
其中,每个带有年份的的 presets 只会编译那个年份批准的 es6 特性,而 env 则可以替代 es2015, es2016, es2017, latest
鉴于我们平时用于转换 es2015 的语法比较多,所以我们可以使用 es2015 ,也可以使用 env ,env 是一个新的 preset ,它可以根据目标的运行环境来自动启用需要的 babel 插件
如果嫌麻烦,我们可以直接使用 es2015 preset ,这样只会转换 es2015 的批准的特性
那么我们的 .babelrc 文件就可以写成
{
"presets": ["es2015"]
}
加入我们想最大程度的支持 es6 的语法,那么我们需要多花费一些心思来配置 Babel,因此我们选用 env 这个 preset,首先先安装:
npm install babel-preset-env --save-dev
接着我们来更改一下 .babelrc 里面的内容
{
"presets": [
["env", {
"targets": {
"node": "current"
}
}]
]
}
注意,如果我们要给 preset 加入一些 option (选项)的话,我们要把它的他的 name 和 options 对象给写进一个数组里
[" preset 的名字",{ options对象 }]
然后,我们执行先前写好的命令:
npm run build
然后我们打开 index-compiled.js ,看看编译后的结果
"use strict";
let axios = require("axios");
axios.get("https://api.douban.com/v2/movie/coming_soon?count=5").then(function (response) {
console.log("最近5部要上映的电影的名字:");
for (let subject of response.data.subjects) {
console.log(subject.title);
}
});
我们发现,除了多了一行 "use strict";
之外并没有什么不同,那是因为目前的 Node.js LTS 版本已经支持 99% 的 ES6 语法,更别说最新的版本了
更多 Node.js 对 ES6 语法支持请查看 http://node.green/
所以我们来更改一下 .babelrc 中 node 的版本:
{
"presets": [
["env", {
"targets": {
"node": 5.12
}
}]
]
}
之后再运行 build 命令 , 打开 index-compiled.js 发现已经有了很多变化
"use strict";
var axios = require("axios");
axios.get("https://api.douban.com/v2/movie/coming_soon?count=5").then(function (response) {
console.log("最近5部要上映的电影的名字:");
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = response.data.subjects[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var subject = _step.value;
console.log(subject.title);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
});
注意:如果 node 参数要遵循
number | "current" | true
{
"targets": {
//"node":"5.12" 这样写是错误的
//还可以写作 "node":"current" 或 "node":true 这两个效果一样
"node": 5.12
}
}
兼容浏览器版本的设置
之前我们已经成功尝试了对 Node.js 的版本进行编译,那么我们怎么对浏览器版本来设置 .babelrc 呢?
之前我们说过,对于 babel-preset-env 他能够目标参数来编译转换那些暂时还不支持的 ES6 语法。然而浏览器环境又比较复杂,所以我们需要使用 browserslist 这个插件来帮我们处理浏览器兼容的选择。
首先,在命令行中输入以下命令来安装:
npm install browserslist --save-dev
安装成功之后我们就可以去编写 .babelrc
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "not ie <= 8"]
}
}]
]
}
我们将原来 targets 下的 node 改为 browsers ,并传入一个数组,数组的成员就是我们要筛选的浏览器的条件,browserslist 支持以下写法,注意大小写不敏感:
- last 数字 versions —— 最近几个版本,如:last 2 versions
- last 数字 浏览器 versions —— 某浏览器的版本,如:last 2 Chrome versions
> 百分比
—— 根据全球浏览器市场份额来决定,如:> 5%> 百分比
in 地区 —— 根据某地区浏览器市场份额,如:> 5% in US- 浏览器 范围 —— 选择指定的版本范围,如:ie 6-8
- 浏览器 表达式 版本 —— 指定满足表达式的浏览器版本,如:Firefox >= 20
- not 表达式 —— 指定排除在外的浏览器版本,如:not ie <= 8
等等...
然后我们再到命令行里去执行 build 命令
发现可以编译没有问题,说明我们的配置是成功的。
Plugins 和 Polyfill
我们之前学会 presets 的使用,现在我们还需要花点时间来讨论下 plugins (插件),polyfill (垫片)
其实 preset 也就是插件的预设,我们甚至可以不使用 presets,完全对每个要转换的语法使用一个插件,但是这样也未必太麻烦了。
与此同时,Babel 默认只转换新的 JavaScript 语法,而不转换新的 API。例如,Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转译。如果想使用这些新的对象和方法,必须使用 babel-polyfill,为当前环境提供一个垫片。
babel-polyfill 会为我们模拟出一个 ES2015 的环境,这样我们就可以使用像 Promise 这样的全新 API 或者像 Array.from 这样的静态方法,或者是Array.prototype.includes 这样的实例方法,Polyfill 会把他们添加到全局作用域里面去。
Polyfill 安装
npm install --save-babel-polyfill
然后就可以在项目里面去使用了:
require("babel-polyfill")
如果你需要利用 ES6 的 import 语句,应该把它写在你的入口文件的最顶端,保证 polyfill 最先加载
import "babel-polyfill"
plugin 我们主要介绍 transform-runtime,transform-runtime 不会修改全局作用域,那它有什么用呢?
在 Babel 帮我们编译 js 文件的时候,会用到一些它自己定义的帮助函数 (helper) 有时候这些函数会重复地出现在模块里,导致编译后的代码体积变大,Babel 为了解决这个问题,transform-runtime 就诞生了。
transform-runtime 不会像 babel-polyfill 那样污染全局环境。而且我们还可以利用 transform-runtime 来无缝地使用 polyfill,只需要写好配置就行,它会把那些新的特性化名到 core-js 中,这样就不会污染全局环境了。
安装
注意:我们需要区分 transform-runtime 的使用环境
如果我们不需要用到 ES6 的一些内建特性,换句话说我们不需要 polyfill 的时候,我们应该引用为开发和测试环境依赖:
npm install babel-plugin-transform-runtime --save-dev
如果我们需要使用像 Promise,set,map 内建特性的时候,我们还需要引用 babel-runtime 为生产环境依赖:
npm install babel-runtime --save
注意:如果你不需要用到 polyfill,你只需要安装babel-plugin-transform-runtime,如果你需要 polyfill,那么你还要安装 babel-runtime 作为生产环境的依赖
配置
安装好之后,我们需要配置一下.babelrc,我们加入以下字段:
{
"plugins": ["transform-runtime"]
}
配置好之后,我们在 index.js 文件内加入一些 ES6 新的内建特性:
var sym = Symbol();
var promise = new Promise;
然后我们在命令行中再次执行 build
命令
var _promise = require("babel-runtime/core-js/promise");
var _promise2 = _interopRequireDefault(_promise);
var _symbol = require("babel-runtime/core-js/symbol");
var _symbol2 = _interopRequireDefault(_symbol);
var _getIterator2 = require("babel-runtime/core-js/get-iterator");
var _getIterator3 = _interopRequireDefault(_getIterator2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
一目了然,当我们的源码使用了 ES6 的一些内建新特性之后,babel-runtime 会帮我们引入它底下 core-js 的相关模块,这样我们就不用单独使用 polyfill 而害怕它污染全局环境。
至此,我们就学会了Babel 的配置和使用。
更多关于 Babel 的用请阅读官方文档 http://babeljs.io/docs/setup/ 从官方的命令行工具到配合其他构建或打包工具,官网都有介绍
编译 != 打包
当我们查看编译好的 js 文件,我们会发现,无论是使用 require 还是 import 命令来导入模块,Babel 似乎并没有帮我们把它转换成 ES5 的语法。据我们所知,目前大部分浏览器都不支持对模块的处理,也就是无法识别 import 这样的命令。
而 Babel 做的事情只是帮我们简单的转换语法而已,至于模块的处理,我们还需要使用其他的工具,例如 Webpack,这样的过程我们称之为打包。
打包的目的就是最终把多个模块组合成一个单独的 js 文件,这样我们就能够在浏览器环境下运行了。
稍安勿躁,之后我们还会在对 Vue 的开发环境的构建中详细阐述 webpack 的使用。