
導入経緯
大きなアプリを開発するにあたり、依存関係を解決してくれるものを探していました。
その中で、
RequireJSや
Browserify、
Componentなども候補に挙がりましたが、
諸々の理由でWebPackを選びました。
特徴の違いについては「WebPack vs Browserify」等で検索してみると良いかと思います。
WebPackのメリット
・どのモジュールシステムでも使える
- AMD
- CommonJS
- UMD
- ES6
- Component
- ...
・どのパッケージマネジャーでも使える
・CSS、画像、フォントなどがrequireできる
・CommonJSシンタックスでモジュールをインポート
var _ = require('lodash');
_.forEach([1, 2, 3], function(num){
});
・Loaders(変換する便利な機能が盛りだくさん)
- CSSプリプロセッサー
- 画像をbase64にエンコード
- ES6→ES5にモジュール変換
- ...
・Loaderの書き方
var $ = require('jquery');
// 画像をbase64にエンコードし、読み込みます
require('url!./image.jpg');
// CSSをテキストとして読み込み、headに入れる
// 複数のLoaderをチェーンすることは可能
require('style!css!./style.css');
// モジュールシステムを使わないJSファイルも
// requireをし、ローカル変数にアサインされます
require('imports?hoge=lib!lib.js');
・Configファイル
インラインのLoaderを毎回書くのは大変なので、
configファイルに書きます。
{
module: {
loaders: [
{ test: /\.png$/, loader: 'url' },
{ test: /\.js$/, loader: 'es6-loader' },
{ test: /\.css$/, loader: 'style!css' }
]
}
}
・モジュールのパス解析
resolve: {
moduleDirectories: ['node_modules', 'bower_components']
}
・長いパスもスッキリ書ける
下記を書くより...
./../../../../library.js
./../../lodash/dist/lodash.js
エイリアスを書くと...
resolve: {
alias: {
library: '絶対パス/../../library',
lodash: 'lodash/dist/lodash.compat.js'
}
}
...すっきりインポートが出来ます。
var lib = require('library');
var _ = require('lodash');
・好きなビルドシステムとの連携が簡単
・賢いビルド出力
- チャンクファイルを等分布することが可能
- 「重いファイルのみ」などのオンデマンド読み込みが可能
- ルートによってセグメントに分割が可能
- 共通モジュールの摘出が可能
- ...
・速いビルド
- インクレメンタルビルド
- 変更されたファイルのみが再ビルドされる
・デバッグが簡単
・懸念点
- 日本語のドキュメントはまだ無い
- 基本的には何でもできる
ということで...
WebPackの導入
さっそくインストールしてみましょう。
公式のサイトは
こちらです。
インストール
次のコマンドをコマンドラインで叩きます。
$ npm install webpack -g
使用方法
WebPackはとても簡単に使用することが出来ます。
$ webpack <input> <output>
<input>はメインのJSファイル名。
<output>はビルドされるJSのファイル名。
では実際に実用的なコードを例にして説明していきたいと思います。
まずはサンプルコードを落とします。
$ git clone https://github.com/gunta/webpack-samples-1pixel.git
そして依存ライブラリをインストールします。
$ npm install
【第1部】CommonJSの簡単なサンプル

$ cd 01-commonjs
フォルダの中に下記ファイルがあります。
<math.js>
exports.add = function (x, y) {
return x + y;
};
<main.js>
var math = require('./math');
alert('1 + 2 = ' + math.add(1, 2));
ビルドをすると、出力先に
bundle.jsが生成されます。
$ webpack main.js bundle.js
そして
main.htmlを開くと、
$ open main.html
きちんとアラートが表示されることが確認できます。
【第2部】Bootstrapを使ったサンプル

$ cd 02-bootstrap
毎度コマンドラインから諸々のフラグを付けるのは大変なので、
Configファイルに書きます。
Configファイル名は
webpack.config.jsにしておくと、
下記コマンドを叩くだけで設定が読み込まれビルドが実行されます。
$ webpack
今回のソースコードは非常にシンプルです。
<main.js>
require('bootstrap/dist/css/bootstrap.css');
こちらの
bootstrap.cssのファイルは、
node_modulesの中に
npm installでインストールしたものですが、
bowerでも手動でも、何でもOKです。
<webpack.config.js>
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
module: {
loaders: [
// 拡張子がCSSの場合はCSSのLoaderを採用
{ test: /\.css$/, loader: 'style!css' },
// bootstrap.cssの中に使うWebFontを(デフォルトで)base64エンコードされます
{ test: /\.svg$/, loader: 'url-loader?mimetype=image/svg+xml' },
{ test: /\.woff$/, loader: 'url-loader?mimetype=application/font-woff' },
{ test: /\.eot$/, loader: 'url-loader?mimetype=application/font-woff' },
{ test: /\.ttf$/, loader: 'url-loader?mimetype=application/font-woff' }
]
}
};
下記コマンドで確認できます。
$ open main.html
【第3部】AngularJSのサンプル

こちらは最新の
公式AngularJSフォルダ設計 ベスト・プラクティスを
ベースにしたサンプルです。
こういう構成にすると、
簡単にコンポーネント化できます。
$ cd 03-angular
<main.js>
var angular = require('angular');
angular.module('app', [
require('./tabs').name,
require('./pane').name
]);
<webpack.config.js>
module.exports = {
entry: './main.js',
output: {
filename: 'bundle.js'
},
module: {
loaders: [
{ test: /\.html$/, loader: 'html' }
]
}
};
<tabs/index.js>
module.exports = angular.module('app.tabs', [])
.directive('tabs', require('./tabs-directive'));
<tabs/tabs-controller.js>
module.exports = function ($scope) {
var panes = $scope.panes = [];
$scope.select = function (pane) {
angular.forEach(panes, function (pane) {
pane.selected = false;
});
pane.selected = true;
};
this.addPane = function (pane) {
if (panes.length == 0) {
$scope.select(pane);
}
panes.push(pane);
}
};
<tabs/tabs-directive.js>
module.exports = function () {
return {
restrict: 'E',
transclude: true,
scope: {},
controller: require('./tabs-controller'),
template: require('./tabs-template.html'),
replace: true
}
};
このように、
.htmlのテンプレートを簡単に埋め込むことができるようになりました。
ビルドをすると、
$ webpack
ブラウザで確認ができます。
$ open main.html
【第4部】CoffeeScriptやLESS、Jadeを使ったサンプル

$ cd 04-coffee-less-jade
<main.coffee>
Colors = require "./colors/colors.coffee"
...
colors = new Colors()
<colors/colors.coffee>
require './colors.less'
template = require './colors.jade'
class @Colors
getTemplate: -> template
module.exports = @Colors
<colors/colors.less>
@import "base";
LESSファイルのインポートまで解析してくれます。
ビルドをします。
$ webpack
ブラウザで確認できます。
$ open main.html
【第5部】コードスプリット

$ cd 05-code-splitting
こちらのサンプルでは、出力先が2つに分割されます。
1つ目のバンドルは、初期に読み込まれ、
2つ目のバンドルは、
require.ensure()を通じて
オンデマンドで必要になった時に読み込まれます。
<main.js>
var a = require('a');
var b = require('b');
require.ensure(['c'], function (require) {
require('b');
var d = require('d');
});
ビルドします。
$ webpack
そして開くと、
$ open main.html
`a b d`が表示されます。
なぜ、
cのモジュールが呼ばれてないのでしょうか?
理由は、
require.ensureでは、ファイルの読み込み完了は保証されるのですが、
中身のコードが評価されないからです。
これは
CommonJSの仕様で、読み込みと評価のタイミングは自由に選べます。
また、WebPackは、
AMDのシンタックスにも対応しています。
require(["a"], function(a) {
// こちらはAMD仕様なので、aモジュールが読み込みも評価もされます
});
個人的にはCommonJSのシンタックスの方が好きです。
【第6部】production/develが最適化されたサンプル

$ cd 06-production
こちらは一番リアルに近いサンプルなので、
じっくり中身を確認してみてください。
色んな機能がフィッチャーされます。
- Gruntによるビルド
- Gulpによるビルド
- Grunt/Gulp無しによるビルド
- 複数ページの複数ファイル出力
- ウォッチもリロードもしてくれるdevサーバー
- Minifyもモジュールの重複排除もしてくれる設定
- jQuery/Bootstrapの読み込み
- グローバル変数にjQuery変数を導入
- 出力チャンクのネーミング
ビルドの仕方は簡単です。
Grunt
Gruntでdevel版をビルドしたい場合
$ grunt
全部のモジュールがビルドされ、develサーバーが立ち上がります。
ファイルを変更すると、自動的にページがリロードされます。
ちなみに、物理的にファイルが出力されず
オンメモリでビルドされるので、速いです。
production版をビルドします。
$ grunt build
dist/フォルダにminifyされたファイルが出力されます。
Gulp
こちらはgruntと同様です。
devel版をビルドします。
$ gulp
production版をビルドします。
$ gulp build
【第7部】EcmaScript 6のサンプル

こちらは未来からきた
ES6の書き方、
ES6モジュールの書き方ができるサンプルです。
ビルドすると現在の
ES5に変換されますので、
未来の書き方を今すぐ使えます。
これは
Angular 2.0でも採用されている方式です。
$ cd 07-es6
まずはBowerのモジュールをインストールします。
$ bower install
そしてビルドします。
$ webpack
中身も未来的です。
<main.js>
// Style modules
require('todomvc-common/base.css')
// ES5 modules
var $ = require('jquery')
var Backbone = require('backbone')
// ES6 modules
import {AppView, Filters} from './todo-app';
// Document ready
$(() => {
new AppView()
new Filters()
Backbone.history.start()
})
<todo-app.js>
// ES5 modules
var $ = require('jquery')
var _ = require('lodash')
var Backbone = require('backbone')
require('backbone.localStorage')
const { Model, View, Collection, Router, LocalStorage } = Backbone
const ENTER_KEY = 13
let TodoFilter = ''
// Todo Model class
class Todo extends Model {
defaults() {
return {
title: '',
completed: false
}
}
toggle() {
this.save({
completed: !this.get('completed')
})
}
}
...
export { AppView, Filters }
終わりに
もっとサンプルがほしい!という方は、
こちらにたくさんあります。
検索可能な限りでは、今回は日本語初のWebPackの記事になりますが、
今担当しているプロジェクトで6ヶ月前から導入していますし、
Instagram.comでの導入事例もあります。
以上、WebPackのお話をさせて頂きました。
最後までお付き合い頂き、ありがとうございました!