ES6 in Depth: Using ES6 Today With Babel and Broccoli

ES6 In Depth: Using ES6 today with Babel and Broccoli

ES6已经存在了,还有些人已经开始谈论 ES7了。新的标准中有哪些特性会被保留,又有哪些闪亮的特性提供?作为一位网络开发人员,我们会对怎么使用它产生疑问。不仅一次,在之前的 深入ES6系列 中,我们一直在鼓励你们开始使用 ES6 来编码,通过用些有意思的工具来实现,我们之前已经有提到这方法:

If you’d like to use this new syntax on the Web, you can use Babel or Google’s Traceur to translate your ES6 code to web-friendly ES5.

今天,我们将一步步地向你展示它是怎么做的。之前提到的工具都称为是 transpilers(transcompiler暂译为 转换编译器)。转换编译器是为人熟知的 资源到资源的编译器,这编译器在不同的编程语言之间进行抽象层面中的转换。转换编译器可以让我们编写ES6的代码,同时也保证代码能够在每个浏览器中运行。

转换编译器是我们的救赎

转换编译器十分易用,你可以按以下描述进行,只要两步:

  1. 我们用ES6语法编写代码
1
2
let q = 99;
let myVariable = `${q} bottles of beer on the wall, ${q} bottles of beer.`;
  1. 我们使用上面的代码作为转换编译器的输入,它会处理这代码,并产出下面的代码:
1
2
3
"use strict";
var q = 99;
var myVariable = "" + q + " bottles of beer on the wall, " + q + " bottles of beer."

我们知道这是旧JS的写法,它能运行于任意的浏览器。

转换编译器内部是怎么从输入到产生,这是是复杂的过程,也超出了这文章的范围。让我们就像开车,而不要去管其所有的内部机械引擎。今天,我们把转换编译器看作是一个黑盒子,来处理我们的代码。

Babel的作用

在项目中使用Bable有不几种不同的方法,首先是命令行工具,你可以通过下面的命令来实现:

1
babel script.js --out-file script-compiled.js

在浏览器中预置Babel的版本也是可用的,你可以将Babel作为常规的js库文件,然后你可以将ES6的代码中的script标签中的类型修改为”text/babel”。

1
2
3
4
\<script src="node_modules/babel-core/browser.js"></script>
\<script type="text/babel">
// Your ES6 code
</script>

但是,这方法不能扩展:你的代码增长时,你会将代码分离成多个文件和目录,这方法是不允许的。这时,你需要一个构建工具,通过管道式来整合Babel。

在下面的部分,我们将Babel整合到一个构建工具中,Broccoli.js,接着我们将通过几个例子来编写并执行我们的ES6代码。当你运行出现错误时,你可以在这里回头查看完整的资源代码:broccoli-babel-examples。在这资源库中,你将找到三个例子项目:

  1. es6-fruits
  2. es6-website
  3. es6-modules

每个会建立在之前的例子之上(没发现)。我们开始会代码最小化,并且会是个一般的解决方案,它可以胜任于为个伟大的项目打关阵。在这文章中,我们将详细地讨论前两个例子。在我们做完之后,你自己可以去阅读、去理解第三个例子中的代码。

如果你认为,你可以等待浏览器支持这些特性,你将会被甩到后面。完全的浏览器支持,如果这会实现,那它将是个漫长的过程。转换编译器已经在这里,新的ECMAScript标准也将计划在年内发布。所以 ,我们将继续、经常地看到新的标准发布出来,而这会先于统一的浏览器平台。跳起来吧,利用这些新的ES6特性吧。

我们的第一个Broccoli 和 Babel项目

Broccoli是个被设计为尽量快的速度来构建项目的工具。你可以压缩和最小化文件,通过这些插件 操作的使用,还有其它很多其它的功能。它帮助我们负担了一大堆的事情,包括处理文件、目录,还有当我们每次修改项目时需要执行的命令。可以把它当成为:

在一定范围上,它类似 Rails 的资源文件的管理,只是它运行于Node,也没有纯粹的后端。

开始项目

Node

正如你猜测一样,你将不得不安装 Node 0.11 或者更高版本。

如果你是在unix系统,避免使用包管理工具,如apt, yum。这样可以避免在安装过程中使用root权限。最好的方法是,从刚才提供的链接地址中,使用你当前的用户手动下载二进制文件。你可以在这里 看到为什么不建议使用 root 权限。你也可以在这里 选择其它的安装方式。

Broccoli

我们首先将建立我们的Broccoli的项目:

1
2
3
4
5
mkdir es6-fruits
cd es6-fruits
npm init
# Create an empty file called Brocfile.js
touch Brocfile.js

现在,我们安装 broccoli 和 broccoli-cli

1
2
3
4
# the broccoli library
npm install --save-dev broccoli
# command line tool
npm install -g broccoli-cli
编写些ES6代码

我们创建 src 目录,接着在里面编辑文件 fruits.js:

1
2
mkdir src
vim src/fruits.js

在我们的新文件中,我们使用 ES6 的语法编写些小的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
let fruites = [
  {id: 100, name: 'strawberry'},
  {id: 101, name: 'grapefruit'},
  {id;102, name: 'plum'}
];

for (let fruit fo fruits) {
  let message = `ID: ${fruit.id} Name: ${fruit.name}`;
  console.log(message);
}

console.lgo(`List total: ${fruits.length}`);

上面的代码使用到了 ES6 的三个特性:

  1. let,用于本作用域内的声明(在之前的文章中已经有讨论过)
  2. for-of 循环
  3. 模板字符串

保存文件,并试着执行它。

1
node src/fruits.js

它还不能运行,但我们希望它可以在Node和任意的浏览器中都能够执行。

1
2
3
let fruits = [
     ^^^^^
SyntaxError: Unexpected identifier
到转换代码的时间了

现在,我们将使用 Broccoli 来加载我们的代码,并通过Babel来处理。我们将编辑文件 Brocfile.js 并添加以下的代码:

1
2
3
4
5
6
7
// import the babel plugin
var babel = require('broccoli-babel-transpiler');

// grab the source and transpile it in 1 step
fruits = babel(src'); // src/*.js

module.export = fruits;

注意到,我们加载了 broccoli-babel-transpiler,这是个 Broccoli 的插件,它包括了 Babel 的库,所以我们必须安装它:

1
npm install --save-dev broccolli-babel-transpiler

现在我们可以构建我们的项目,并通过下面代码来执行脚本:

1
2
broccoli build dist # compile
node dist/fruits.js # execute ES5

这个输出会看到:

1
2
3
4
ID: 100 Name: strawberry
ID: 101 Name: grapefruit
ID: 102 Name: plum
List total: 3

这十分的简单!你可以打开 dist/fruits.js来看看,转换后的代码文件是什么样子。Babel转换编译器的其中一个好的特性就是可产生良好可读性的代码。

为网站编写ES6代码

在第二个例子中,我们将提升个等级。首先,退出 es6-fruits 目录,并参照之前的【项目开始】的步骤来创建一个新的目录 es6-website

在 src 的目录中,我们创建三个文件:

src/index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html>
    <head>
      <title>ES6 Today</title>
    </head>
    <style>
      body {
        border: 2px solid #9a9a9a;
        border-radius: 10px;
        padding: 6px;
        font-family: monospace;
        text-align: center;
      }
      .color {
        padding: 1rem;
        color: #fff;
      }
    </style>
    <body>
      <h1>ES6 Today</h1>
      <div id="info"></div>
      <hr>
      <div id="content"></div>

      <script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
      <script src="js/my-app.js"></script>
    </body>
</html>

src/print-info.js

1
2
3
4
5
6
7
function printInfo() {
  $('#info')
  .append('<p>minimal website example with' +
                'Broccoli and Babel</p>');
}

$(printInfo);

src/print-colors.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// ES6 Generator
function* hexRange(start, stop, step) {
  for (var i = start; i < stop; i += step) {
    yield i;
  }
}

function printColors() {
  var content$ = $('#content');

  // contrived example
  for ( var hex of hexRange(900, 999, 10) ) {
    var newDiv = $('<div>')
          .attr('class', 'color')
          .css({ 'background-color': `#${hex}` })
          .append(`hex code: #${hex}`);
    content$.append(newDiv);
  }
}

$(printColors);

你应该注意到这一点:function* hexRange。是的,这是 ES6 的生成器。这一个特性并没有在所有的浏览器得到支持。为了能够使用它(生成器),我们需要个工具。Babel 可以提供,我们也很快地使用到它。

下一步是合并所有的JS文件,并在网站中使用它。这最难的部分是编写我们的Brocfile 文件。这时,我们安装四个插件:

1
2
3
4
npm install --save-dev broccoli-babel-transpiler
npm install --save-dev broccoli-funnel
npm install --save-dev broccoli-concat
npm install --save-dev broccoli-merge-trees

然后,使用它们 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Babel transpiler
var babel = require('broccoli-babel-transpiler');
// filter trees (subsets of files)
var funnel = require('broccoli-funnel');
// concatenate trees
var concat = require('broccoli-concat');
// merge trees
var mergeTrees = require('broccoli-merge-trees');


// Transpile the source files
var appJs = babel('src');


// Grab the polyfill file provided by the Babel library
var babelPath = require.resolve('broccoli-babel-transpiler');
babelPath = babelPath.replace(/\/index.js$/, '');
babelPath += '/node_modules/babel-core';
var browserPolyfill = funnel(babelPath, {
  files: ['browser-polyfill.js']
});

// Add the Babel polyfill to the tree of transpiled files
appJs = mergeTrees([browserPolyfill, appJs]);
// Concatenate all the JS files into a single file
appJs = concat(appJs, {
  // we specify a concatenation order
  inputFiles: ['browser-polyfill.js', '**/*.js'],
  outputFile: '/js/my-app.js'
});

// Grab the index file
var index = funnel('src', {files: ['index.html']});


// Grab all our trees and
// export them as a single and final tree
module.exports = mergeTrees([index, appJs]);

是时候构建并执行我们的代码了:

1
broccoli build dist

这里,你应该在dist目录中看到如下的结构:

1
2
3
4
5
$> tree dist/
dist/
|--index.html
|--js
|--|-- my-app.js

这是个静态的网站,你可以部署到任意的服务器中,检查这代码是可行的。例如,

1
2
3
cd dist/
python -m SimpleHTTPServer
# visit http://localhost:8000/

你可以看到:

es6 today

更多乐趣在 Babel 和 Broccoli 中

上面的第二个例子提供我们一方法来怎么使用Babel,从而完成项目。它应该可以让你理解一些时间了,如果你通过Babel、Broccoli更多地使用ES6,你可以查看一下这个项目:broccoli-babel-boilerplate。它也是用 Broccoli + Babel 开始的,它至少提高了两个点。这模板例子包括了 模块、引入、和单元测试。

你可以试试这里的配置例子:es6-modules,Brocfile 有很多魔法的地方,其类似与我们上面做的事件。

Comments