深入淺出 ES6:ES6 與 Babel / Broccoli 的聯(lián)用

深入淺出 ES6 指的是添加在 ECMASript 標(biāo)準(zhǔn)第六版中的 JavaScript 編程語言的新特性,簡稱為 ES6。

雖然 ES6 剛剛到來,但是人們已經(jīng)開始談?wù)?ES7 了,它未來的樣子,以及新標(biāo)準(zhǔn)能提供哪些光鮮亮麗的特性。作為網(wǎng)絡(luò)開發(fā)者,我們想的則是如何將這些特性都用上。在之前的深入淺出 ES6 貼文中,我們曾多次鼓勵讀者朋友使用 ES6 在一些小工具的幫助下編寫代碼。并這樣調(diào)戲你們:

如果你想在網(wǎng)絡(luò)上使用這個新語法,你可以使用 Babel (一款 JS 編譯器)或谷歌的 Traceur (一款編譯器)將你的 ES6 代碼翻譯成網(wǎng)絡(luò)友好型的 ES5。

今天,本文將向你展示詳細(xì)的實現(xiàn)步驟。上面所提到的工具被稱為轉(zhuǎn)換編譯器。轉(zhuǎn)換編譯器又被稱為源到源的編譯器 —— 將編程語言在同一個抽象層面進行轉(zhuǎn)換的編譯器。轉(zhuǎn)換編譯器使得我們在使用 ES6 編寫代碼的同時,保證代碼在每一個瀏覽器中都能夠運行。

一、我們的救星——轉(zhuǎn)換編譯器

轉(zhuǎn)換編譯器使用簡便。兩個步驟就能解釋清楚:

1.用 ES6 語法寫出代碼。

let q = 99;
let myVariable = `${q} bottles of beer on the wall, ${q} bottles of beer.`;

2.將上面的代碼作為轉(zhuǎn)換編譯器的輸入,編譯器處理之后得到下面的輸出:

"use strict";

var q = 99;
var myVariable = "" + q + " bottles of beer on the wall, " + q + " bottles of beer."

這是我們熟知的 JavaScript,它能夠在任何瀏覽器中使用。

至于轉(zhuǎn)換編譯器內(nèi)部如何轉(zhuǎn)換代碼及其復(fù)雜,遠(yuǎn)非這篇小文能夠解釋得了。這就像我們不知道內(nèi)置引擎的工作原理,但是我們卻能夠很好地駕駛車輛。因此,我們也可以將轉(zhuǎn)換編譯器看做一個能夠處理代碼的黑盒子。

Babel登場

在一個具體的項目中,使用Babel有幾種不同的方法。在工具中,有一種命令行工具,你能按照如下所示的格式發(fā)出命令:

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

你也可以使用瀏覽器版本。只需要將 Babel 添加為常用 JS 庫,然后將你的 ES6 代碼放入「 text/babel」類型標(biāo)簽中。如下所示:

<script src="node_modules/babel-core/browser.js"></script>
<script type="text/babel">
// Your ES6 code
</script>

當(dāng)你的代碼庫開始膨脹,你不得不將它們放在不同的文件和文件夾中時,這些方法都不會跟著膨脹。那時,你需要的是一個開發(fā)工具以及一個使用管道來整合Babel 的辦法。

在接下來的內(nèi)容中,我們會將 Babel 整合至開發(fā)工具,即 Broccoli.js,并書寫和執(zhí)行 ES6 代碼例子。為了不讓你感到迷惑,你可以在這里看到完整的源代碼:broccoli-babel-實例。在那里你會找到三個項目實例:

1.es6-fruits

2.es6-website

3.es6-modules

每個實例都建立在前一個例子的基礎(chǔ)之上。我們從最小最基礎(chǔ)的步驟開始,然后發(fā)展成一個通用的方案(作為雄偉計劃的第一步)。本文將會詳細(xì)描述前兩個實例。此后,你就能獨立閱讀和理解實例3中的代碼了。

如果你現(xiàn)在的想法是 —— 我就坐等瀏覽器來支持新特性吧 —— 那你就會掉隊。即使最終實現(xiàn)了完全兼容,也必須花費很長的時間。而轉(zhuǎn)換編譯器卻一直在這兒;而且 ECMAScript 標(biāo)準(zhǔn)會按計劃每年發(fā)布新版本。因此,我們將會看到的是在統(tǒng)一的瀏覽器平臺出現(xiàn)之前新標(biāo)準(zhǔn)的不斷涌現(xiàn)。不妨現(xiàn)在就加入我們開始使用這些新特性吧。

二、第一個 Broccoli & Babel 項目

Broccoli 是一個旨在實現(xiàn)項目開發(fā)的工具。通過使用 Broccoli 插件,你能夠壓縮合并、最小化文件等等。它節(jié)省了我們處理文件、目錄以及每次項目改變后都要執(zhí)行命令的時間。可以將它視為:

這就像是鐵路管道資產(chǎn),不過它在節(jié)點上運行而且與后端無關(guān)(backend-agnostic)。

1、項目設(shè)置

節(jié)點

你也許已經(jīng)猜到了,你必須安裝 Node0.11 或后繼版本。

如果你的系統(tǒng)是 Unix,不要選擇程序包管理器(如 apt,yum)安裝。這是為了避免在安裝時使用根管理權(quán)限。最好是在當(dāng)前用戶下使用上面提供的鏈接手動安裝。你可以在 Do notsudo npm 了解為什么我們不推薦使用根權(quán)限。在那里你能找到其他的安裝方法

Broccoli

我們首先使用下面的代碼安裝 Broccoli 項目:

mkdir es6-fruits
cd es6-fruits
npm init
# Create an empty file called Brocfile.js
touch Brocfile.js

然后安裝 broccoli 和 broccoli-cli

# the broccoli library
npm install --save-dev broccoli
# command line tool
npm install -g broccoli-cli

寫入 ES6 代碼

新建 src 文件夾并放入文件 fruits.js

mkdir src
vim src/fruits.js

在這個新文件中用 ES6 語法敲一段代碼。

let fruits = [
  {id: 100, name: 'strawberry'},
  {id: 101, name: 'grapefruit'},
  {id: 102, name: 'plum'}
];

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

  console.log(message);
}

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

上面的代碼樣本使用了3個 ES6 的特性:

  1. 使用 let 在局部范圍內(nèi)聲明(在后續(xù)的文章中我們將會進一步討論)

  2. for-of 循環(huán)

  3. 模板字符串

保存并嘗試運行。

node src/fruits.js

現(xiàn)在還不能運行,但是馬上我們將會使用節(jié)點(Node)讓它在任何瀏覽器中都可以執(zhí)行。

let fruits = [
    ^^^^^^
SyntaxError: Unexpected identifier

2、轉(zhuǎn)換編譯器時間

現(xiàn)在,我們將會使用 Broccoli 加載我們的代碼并推向 Babel。編輯文件 Brocfile.js,加入這段代碼:

// 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.exports = fruits;

注意我們需要使用 broccoli-babel-transpiler,即一個封裝了 Babel 庫的 Broccoli 插件,因此我們必須安裝:

npm install --save-dev broccoli-babel-transpiler

現(xiàn)在構(gòu)建項目并執(zhí)行代碼:

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

輸出應(yīng)該長這樣:

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

這很簡單嘛!你可以打開 dist/fruits.js,看一看轉(zhuǎn)換的代碼長啥樣。Babel 轉(zhuǎn)換編譯器的一個不錯的特性就是它能夠得到可讀的代碼。

三、在 website 中寫入 ES6 代碼

在第二個實例中我們將會提高一個姿勢。首先,退出 es6-fruits 文件夾并重復(fù)上面的步驟新建一個 es6-website 目錄。

在 src 文件夾中新建三個文件:

src/index.html

<!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="http://code.jquery.com/jquery-2.1.4.min.js"></script>
    <script src="js/my-app.js"></script>
  </body>
</html>

src/print-info.js

function printInfo() {
  $('#info')
  .append('<p>minimal website example with' +
          'Broccoli and Babel</p>');
}

$(printInfo);

src/print-colors.js

// 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);

你可能已經(jīng)注意到了這段代碼:function* hexRange — yes,這是一個 ES6 生成器。現(xiàn)階段,并不是所有的瀏覽器都支持這個特性。為了使用它,我們需要一個緩沖。Babel 提供這個緩沖而且我們很快就會用到。

接下來便是整合所有的 JS 文件并在一個網(wǎng)站中使用它們。最難的部分是寫 Broc 文件。這次我們安裝四個插件:

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

讓它們開始干活:

// 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]);

是時候執(zhí)行我們的代碼了。

broccoli build dist

This time you should see the following structure in the distfolder:

這次你應(yīng)該在dist文件夾中看到如下的結(jié)構(gòu):

$> tree dist/
dist/
├── index.html
└── js
    └── my-app.js

這是一個靜態(tài)網(wǎng)站,你能夠使用服務(wù)器來證明代碼在工作中。例如:

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

你將會看到這個:

深入淺出 ES6:ES6 與 Babel / Broccoli 的聯(lián)用
深入淺出 ES6:ES6 與 Babel / Broccoli 的聯(lián)用

四、Babel 和 Broccoli 中更多好玩兒的

這第二個實例展示了使用 Babel 能完成很多事情。這可能會讓你繼續(xù)探索一會兒。如果你想繼續(xù)玩 ES6,Babel 和 Broccoli,你應(yīng)該去看看這個:broccoli-babel-boilerplate。這也是一個 Broccoli+Babel 的設(shè)置,而且高級得多。這個樣本文件會處理模塊,導(dǎo)入以及進行單元測試。

你也可以試試這個已有的配置實例:es6-modules。所有的高科技都在 Broc 文件夾里面,而且這和我們剛做的差不多。

OneAPM 助您輕松鎖定 .NET 應(yīng)用性能瓶頸,通過強大的 Trace 記錄逐層分析,直至鎖定行級問題代碼。以用戶角度展示系統(tǒng)響應(yīng)速度,以地域和瀏覽器維度統(tǒng)計用戶使用情況。想閱讀更多技術(shù)文章,請訪問 OneAPM 官方博客。
本文轉(zhuǎn)自 OneAPM 官方博客
原作者:Gastón I. Silva

原文鏈接:https://hacks.mozilla.org/2015/06/es6-in-depth-babel-and-broccoli/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容