2015-11-21 babel presetsに合わせて更新
そろそろ ES6 で書きたいので環境メモを残しておきます。
はじめに
このエントリでは Node.js を使用してフロントエンドで ECMAScript 6th を書くことを目的にしています。その際に babel を使います。
babel は grunt, gulp, browserify など多くのプラグインが提供されていますが、ここでは grunt を使っていきます。
以前は RequireJS でモジュール書いて r.js (1ファイルにまとめるやつ) でコンパイル(minify) てやってたのですが、
今回 webpack と babel-loader を使った ES6 環境で進めようと思います。
インストール
Node.js はすでにインストール済み前提とします。まず適当なところにディレクトリを作って移動して
npm init
で package.json を作成します。
$ mkdir my_project
$ cd my_project
$ npm init
# いろいろ聞かれるけどとりあえずエンター連打
$ ls
package.json # できてること確認
ファイル構成
ファイル構成は以下のような感じです。$ tree
.
├── package.json
├── Gruntfile.js # これから作るgruntタスク
└── public # 公開ディレクトリ
├── index.html # これから作るhtml
├── js # jsソースを置くディレクトリ
└── assets # コンパイル済みのjsなどを置くディレクトリ
まだ package.json しか作ってないので、public
ディレクトリと、
その中に js
, assets
を作ります。
今回使うタスクランナーの grunt をインストールします (
-g
をつけてグローバルで)。
$ npm install -g grunt-cli
続いて grunt-webpack や grunt-contrib-uglify (圧縮),
grunt-contrib-watch (ファイルの更新を監視) などをインストールします。
本来なら webpack と grunt-webpack だけで済むっぽいですが、
非圧縮ファイルを確認したいときがあるので webpack で1ファイルにまとめてから uglify で圧縮します。
あえて非圧縮ファイルを残します。
$ npm install --save-dev grunt grunt-contrib-uglify grunt-contrib-watch
--save-dev オプションを指定して package.json の devDependencies に追加します。
それから webpack, babel-loaderをインストールします(1回でまとめてインストールしても同じです)。
$ npm install --save-dev grunt-webpack webpack babel-loader babel-preset-es2015
Gruntfile.js
プロジェクトのルート (package.json と同じ階層)にGruntfile.js
を作成します。
// Gruntfile.js
var webpack = require('webpack');
module.exports = function(grunt) {
var pkg = grunt.file.readJSON('package.json');
grunt.initConfig({
// webpackの設定
webpack: {
build: {
progress: true,
entry: {
// メインjsファイル (ここでは app.js とする)
app: './public/js/app.js'
},
output: {
// 出力ファイル (ここでは assets/js に出力)
path: './public/assets/js',
filename: 'bundle.js'
},
module: {
loaders: [{
// .jsに対してbabelを指定してES6で書けるようにする
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015']
}
}]
},
resolve: {
// 読み込む際に拡張子を省略できるようにする
extensions: ['', '.js']
}
}
},
// JS圧縮(minify)/最適化
uglify: {
options: {
// Source Mapをつける
sourceMap: true,
sourceMapName: './public/assets/js/bundle.map'
},
build: {
files: {
// 出力ファイル: 元ファイル
'./public/assets/js/bundle.min.js': './public/assets/js/bundle.js'
}
}
},
// ファイルの変更/更新を監視
watch: {
js: {
files: [
// js/ 以下を再帰的に監視する
'./public/js/**/*.js',
],
tasks: ['webpack', 'uglify']
}
}
});
grunt.loadNpmTasks('grunt-webpack');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
// デフォルトタスクの登録
grunt.registerTask('default', ['webpack', 'uglify']);
};
上のコードで Gruntfile.js に書き込んで保存します。
ES6でコードを書く
せっかくなので es6 modules を使って書いてみます。public/js
に app.js
を作成します。
// app.js
import * as math from './lib/math';
// Template Strings
var msg = `2π = ${math.sum(math.pi, math.pi)}`;
alert(msg);
続いて
public/js/lib
(lib ディレクトリを作成) に
math.js
を作成します。
// lib/math.js
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
public
に index.html
を作成します。
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>webpack es6 babel-loader example</title>
<script src="assets/js/bundle.min.js"></script>
</head>
<body>
<h1>webpack es6 babel-loader example</h1>
</body>
</html>
Build
各ファイルを作成したらgrunt
を実行してみます。
単に grunt と実行すると、default のタスクが実行されます。
$ grunt
...
Running "webpack:build" (webpack) task
...
Running "uglify:build" (uglify) task
...
>> 1 file created.
Done, without errors.
みたいに表示されれば成功です。
結果
index.html
をブラウザで開くと、2π = 6.283186
とalertで表示されます。
出力
public/assets
に以下の3つのファイルが作られています。
- bundle.js : 非圧縮のファイル
- bundle.map : Source Mapファイル
- bundle.min.js : 圧縮済みのファイル
html 内では圧縮されたファイル
assets/js/bundle.min.js
を読み込んでいますが、
非圧縮の bundle.js を見ると変換されたコードも確認できます。
ファイルの変更を監視
毎回grunt
と入力するのも手間になるので、以降はファイルの変更を監視するようにします。
$ grunt watch
を実行すると、ファイルが更新される度に自動でタスクを実行してくれます。
Ctrl+C
で中止できます。
babel-runtimeを含める
Promise などを使う場合、babel の runtime (polyfill) を含めるように指定します。runtime を使う場合は babel-runtime のインストールが必要です。
$ npm install babel-runtime --save
Gruntfile.js
の webpack loader を修正します。
// Gruntfile.js
...
// webpackの設定
webpack: {
build: {
...
module: {
loaders: [{
// .jsに対してbabel-loaderを指定してES6で書けるようにする
test: /\.js$/,
exclude: /node_modules/,
// ランタイムを含めるよう指定する
loader: 'babel-loader?experimental&optional=selfContained'
}]
},
...
}
},
...
runtime を含めると、それなりにサイズがあるので個別でライブラリ等を使うのもいいかもしれません。
Demo
今回作成した動作デモ:Source
今回作成したソース:おわりに
フロントエンド周りは移り変わりが激しいですが、安定したものを使っていきたいところです。RequireJS 使った r.js の最適化は魅力的なのですが、 あらかじめひとまとめにする前提ってなると webpack が便利に感じます。
最近は CoffeeScript 書くことが多くて Coffee も楽しいなぁと思いつつ、 やっぱり ES6 で書けると嬉しいです (まだまだ勉強中…)。