2

首先,作为入门的话,Babel的用户手册是个很不错的选择,里面基本覆盖了Babel使用的各方面。所以下面主要是我学习Babel的一些笔记,姑且当作是一篇入门吧。

Babel是什么

按照Babel官网的说法,Babel是一个Javascript编译器。它可以把用最新标准编写的Javascript代码编译成现在的浏览器或者node环境下能运行的代码,这个过程叫做“源码到源码”编译,又称转译(transpiling)。通过这个方式,我们就可以提前使用下一代的标准和特性进行编码,然后在现在的环境下运行。

安装Babel

通常我们使用Babel的babel-cli工具在命令行下进行文件的编译。我们可以对babel-cli进行全局安装,也可以把它安装到项目里。这里我选择了把它安装到项目里。这样做的好处是:

  • 不同的项目可能会使用不同版本的Babel,使用全局的话只能使用一致的版本

  • 方便项目的部署,使用全局安装的话意味着对环境有个隐式的依赖。

除了babel-cli,还有其他使用Babel的方式,具体可以看用户手册。

下面我们开始安装babel-cli。首先新建一个工作目录,并创建package.json文件:

$ npm init -y

安装babel-cli

$ npm install --save-dev babel-cli

安装完我们可以这样运行:

$ ./node_modules/babel-cli/bin/babel.js -V
6.11.4 (babel-core 6.11.4)

但是这样运行很不方便,我们可以通过npm scripts来运行Babel。在package.json文件里,我们增加scripts字段,并添加一个脚本:

{
  "scripts": {
    "build": "babel src -d lib"
  },
  "devDependencies": {
    "babel-cli": "^6.11.4"
  }
}

这里增加了一条名字为build的脚本,命令的内容是把src里的文件通过Babel转译到lib目录里。然后我们可以通过以下命令运行脚本:

$ npm run build

当然,现在运行这个命令会报错,因为我们并没有src文件夹。下面我们正式进入正题。

Babel的基础使用

转译初探

首先我们创建src目录,然后创建一个js文件index.js

[1,2,3].map(n => n + 1);

然后运行npm run build命令,就会看到lib文件夹里多了一个转译后的文件index.js。但是打开来看会发现这个转译后的文件跟源文件并没有区别。因为Babel需要你通过插件(Plugin)或者预设(presets)告诉它做什么。例如我们可以通过babel-preset-es2015告诉Babel把ES2015的文件转译成ES5。这也是Babel最常用的一个用法之一。

配置文件

要使用插件或者预设(相当于一组插件),我们需要在Bable的配置文件里面进行配置。有两个方式进行配置。

第一个方式是通过.babelrc文件:

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

第二个方式是使用package.json文件:

{
  "name": "my-package",
  "version": "1.0.0",
  "babel": {
    "presets": [],
    "plugins": []
  }
}

使用预设

上面说过,预设就是一组插件的集合,例如预设babel-preset-es2015就是把一堆跟ES2015有关的插件组合起来提供编译ES2015代码为ES5代码的功能。下面我们开始使用babel-preset-es2015预设来把ES2015转译成ES5。

首先我们安装这个预设:

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

然后在配置文件里添加这个预设:

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

最后我们再次运行一次npm run build命令,再次打开lib/index.js文件,我们会看到代码已经被编译成ES5的语法:

"use strict";

[1, 2, 3].map(function (n) {
  return n + 1;
});

Babel的执行

Polyfill

像上面那段转译后的代码我们可以直接使用在当前的环境下,但是并不是所有转译后的文件我们都只能直接使用,因为虽然Babel可以编译目前几乎所有的ES2015语法,但是一些新的API可能在当前的Javascript环境下无法支持。例如下面的代码(假设文件为lib/index.js):

function addAll() {
  return Array.from(arguments).reduce((a, b) => a + b);
}

转译后会变成:

function addAll() {
  return Array.from(arguments).reduce(function(a, b) {
    return a + b;
  });
}

可以看到语法上已经转译成ES5了,但是并不是所有的Javascript环境都支持Array.from,例如我们在IE上运行如下页面:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <script src="lib/index.js"></script>
        <script>
            console.log(addAll(1, 2, 3, 4, 5));
        </script>
    </body>
</html>

可能会报以下的错误:

对象不支持“from”属性或方法

Babel的解决方法就是使用Polyfill技术(使用了core-jsregenerator),通过在当前的运行环境模拟不存在的API来达到使用新API的目的。

首先我们安装babel-polyfill

$ npm install --save babel-polyfill

注意这里使用的是--save而不是--save-dev,因为我们需要在代码里引入babel-polyfill。我们需要在文件顶部导入它:

import "babel-polyfill";

在添加这句代码后,上面的代码在转译后会变成下面这个样子:

"use strict";

require("babel-polyfill");

function addAll() {
  return Array.from(arguments).reduce(function (a, b) {
    return a + b;
  });
}

因为Babel编译时默认使用的是CommonJS的模块规范,所以会看到转译后的代码使用了require方法来加载babel-polyfill。这个在node环境下运行没有问题,但是在浏览器环境下运行就会报错,因为浏览器目前还不原生的支持模块的加载。那在浏览器下怎么使用babel-polyfill呢?

我们可以把babel-polyfill通过外部js的方式加载进来,而不是在js代码里进行引入:

<script src="node_modules/babel-polyfill/dist/polyfill.min.js"></script>
<script src="lib/index.js"></script>

但是如果你打算在项目里使用模块,上面明显不是很好的解决方案。下面我们看看如果解决在浏览器环境下模块的加载问题。

使用模块

要在浏览器环境下加载依赖模块,有很多方式,例如使用webpack或者browserify之类的打包工具(建议的方式,但是这里我们先不涉及)。或者我们可以使用浏览器端的模块加载器进行加载,例如我们使用AMD模块规范进行编译,然后用RequireJS进行加载。下面我使用SystemJS来做例子。

首先我们安装es2015-modules-systemjs插件:

$ npm install babel-plugin-transform-es2015-modules-systemjs

然后修改配置文件,在plugins里添加插件:

{
  "plugins": ["transform-es2015-modules-systemjs"]
}

我们修改下lib/index.js,把addAll方法导出为模块的方法:

import "babel-polyfill";

export function addAll() {
  return Array.from(arguments).reduce((a, b) => a + b);
}

再次对文件进行编译,编译后的lib/index.js变成这样:

"use strict";

System.register(["babel-polyfill"], function (_export, _context) {
  "use strict";

  return {
    setters: [function (_babelPolyfill) {}],
    execute: function () {
      function addAll() {
        return Array.from(arguments).reduce(function (a, b) {
          return a + b;
        });
      }

      _export("addAll", addAll);
    }
  };
});

然后我们需要在HTML里引入system.js。由于SystemJS依赖于Promise,它会加载目录下system-polyfills.js文件,所以我们需要确保这个文件的存在。在加载system.js后,我们就可以使用SystemJS进行模块的加载了:

<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <script src="system.js"></script>
        <script>
            System.config({
                baseURL: './lib',
                map: {
                    'babel-polyfill': './node_modules/babel-polyfill/dist/polyfill.min.js'
                }
            });

            System.import('index.js').then(function(m) {
                console.log(m.addAll(1, 2, 3, 4, 5)); // 15
            });
        </script>
    </body>
</html>

出于性能的考虑,也可以用像Bluebirdes6-promise这样的polyfill做替代,在system.js之前加载。

后记

自此,我们学习到了Babel的一些基础用法,包括安装和运行,以及配置和预设的用法,同时也初探了一些编译后的文件的运行问题。但是要用好Babel,还有很多问题需要继续探讨,期待我的下一篇笔记。

参考

https://babeljs.io/
https://github.com/thejamesky...
https://github.com/systemjs/s...


icyfire
1.4k 声望53 粉丝