Mach3.laBlog

はじめてのモジュールバンドラー

近年流行りだした「モジュールバンドラー」ですが、いまでは必携と言えるほどの存在になりつつあります。 今回はそんなモジュールバンドラーをざっくりと紹介します。

  1. モジュールバンドラーとは
  2. どんな時に使いたい?
  3. 「requireを使える」ということ
  4. 様々なモジュールバンドラー
  5. browserify を使ってみよう
  6. browserify + Grunt で自動コンパイルする
  7. まとめ

モジュールバンドラーとは

モジュールバンドラー(module bundler)は、文字通りモジュールをひとまとめにする(bundle)ツールです。 あまり用語として聞き慣れませんが、モジュールバンドラーの代表格のひとつである webpack でも使用されている言葉です。 ひらたく言うと、JavaScriptの依存関係を解決し、それらをインクルードしたひとつのファイルにまとめてくれます。

上の図のように、 require で表現された依存関係を解決してゆき、 コンパイルする際にそれらを全て内包したファイルを作ってくれるのが一般的なモジュールバンドラーの振る舞いです。 require はもともとサーバーサイドJavaScript用に考えられた仕様なので、基本的にブラウザ上では動きません。 それをブラウザ上でも利用可能にするというのが主な目的です。

どんな時に使いたい?

1. JavaScriptファイルを連結したい

旧来JSファイルを連結する際には、まとめたいファイルを指定してシンプルにファイルを繋げていましたが、 この方法には「連結するファイルリストを管理しなければならない」という煩わしさがあります。

一方でモジュールバンドラーの場合はコード内の require を辿って必要なファイルだけを梱包してくれるので、 ファイルリストの管理から解放され、モジュールの取捨選択も一行の追加・削除だけで解決できます。

2. JavaScriptでモジュールのインポートをしたい

他言語で importrequire を使っている方は「モジュールのインポートができる」事の素晴らしさも十分にご承知だと思います。 要するに、一行加えるだけで別ファイルに記述された機能がそのファイル内で利用できる様になるのです。

また同時に、コードを見直す際などに依存関係が一目瞭然になるのも大きな利点です。 Webアプリケーションの開発ではJavaScriptファイルが肥大化・複雑化するので、なんとしても欲しい仕組みですね。

3. パッケージマネージャーを npm で一本化したい

bower 等のパッケージマネージャーを利用されている方は一度はその管理に頭を悩ませるでしょう。 bower はそれ自身を npm でインストールして、.bowerrc と bower.json で設定を行って管理するのですが、 「どうせ npm で package.json があるんだからそっちで一本化したい」となるわけですね。

モジュールバンドラーは基本的に node_modules ディレクトリからモジュールを探してインポートするので、 全て npm だけで解決する事ができ、管理が大変楽になります。

4. node.js のモジュールをブラウザ上で使いたい

node.js 向けに作られたモジュールも(一部をのぞいて)インポート可能です。

まとめると

プロジェクトの規模が大きければ大きいほど、より効力を発揮すると言えそうです。

「requireを使える」ということ

node.js に明るくない方の為に簡単に補足をしておくと、 require / exports というのは node.js で使われているモジュールの読み込みの仕組みで、CommonJSなどと呼ばれていたりします。 例えばみんなだいすきjQueryを読み込みたい場合は、まず npm で jquery をインストールします。

$ npm install jquery@1 --save

そしてJavaScript側で require します。

var $ = require("jquery");

これだけで、そのファイル内ではいつも通りに $ でjQueryを参照する事ができます。簡単ですね。

また、「そのファイル内では」という所がポイントで、上の例の記述はグローバルな空間を汚染しません。 つまり、そのファイルのコンテキスト外で jQuery を利用しようとしても、参照出来ないという事です。

ローカルモジュールを組み込む

npm で導入したのではない、自前のモジュールを読み込みたい場合は、そのファイルまでの相対パスで指定します。

var utils = require("./modules/utils");

ここで注意したいのは、相対パスを “./” からはじめている部分です。 これが “modules/utils” だとモジュールの名前だと判断され、 “modules/utils” という名前のモジュールを node_modules から探そうとしてしまいます。

require できるローカルモジュールを作る

モジュールの宣言は exports あるいは module.exports に代入して行います。 まずは exports から使ってみましょう。

// modules/utils.js
exports.foo = function(){ return "foo"; };
exports.bar = function(){ return "bar"; };

このように書く事で、require の返り値がプロパティに foo と bar を備えたオブジェクトになります。 要するに、モジュール内で宣言されている exports オブジェクトを返しているわけですね。

var utils = require("./modules/utils");
utils.foo(); // => "foo"

対して module.exports は、返り値そのものを書き換えたい場合、たとえばオブジェクトではなく関数等にしたい時に有効です。 クラスを返したいケースなどで使えますね。

// modules/foo.js
var Foo = function(){ ... };
module.exports = Foo;

このようにすると、返り値は Foo 関数そのものとなります。

var Foo = require("./modules/foo");
var foo = new Foo();

様々なモジュールバンドラー

browserify

Browserifyhttp://browserify.org/
Browserify lets you require(‘modules’) in the browser by bundling up all of your dependencies.

  • 単機能な方向性で、シンプルで簡単
  • Gulpとの相性はイマイチです

webpack

webpackhttps://webpack.github.io/
webpack, the flexible module bundler

  • JSのバンドルに留まらず多機能ですが、設定が少々ややこしい
  • Gruntとの相性はイマイチです
  • AMD仕様でのモジュール定義も可能
  • browserifyと比較して速度でアドバンテージがある様です

RequireJS という物もありましたが、モジュールの定義がCommonJSではなく本記事に沿わないので割愛します。 browserifyは単機能でシンプル、webpackは多機能という印象です。

browserify を使ってみよう

シンプルな browserify を実際に使ってみましょう。 前提条件として、node.js がインストールされている必要があります。

1. インストール

ターミナル等で browserify をインストールします。環境によっては sudo を頭に付ける必要があります。

$ npm install -g browserify

インストールが完了すれば browserify コマンドが使用可能になります。

$ browserify --help
Usage: browserify [entry files] {OPTIONS}

2. 実行する

適当なJSファイルを用意して、browserify コマンドでコンパイルしてみましょう。

$ browserify test.js -o bundle.js

こちらが基本形です。上のコマンドを実行すると、test.js をコンパイルした結果が bundle.js として保存されます。

browserify + Grunt で自動コンパイルする

browserify コマンドを毎度実行するのは面倒なので、 タスクランナー(Grunt)でファイルの更新を検知して自動的にコンパイルする環境をつくります。 (前述にもありますが、Gulp + browserify は少々手間がかかり説明に不向きなので、ここでは Grunt で説明します)

ファイル構成例

/
├ html/
│ └ assets/
│      └ js/
│           ├ src/
│           │ ├ modules/
│           │ │ ├ mymodule-01.js
│           │ │ └ mymodule-02.js
│           │ └ main.js ... エントリーファイル
│           └ main.js ... コンパイル先のJSファイル
├ node_modules/
└ Gruntfile.js

設定

まず必要なモジュールをインストールします。

$ npm install grunt grunt-browserify grunt-contrib-watch --save-dev

Gruntfile.js を用意します。タスクの設定は下記のような感じにしてみます。

grunt.initConfig({

    browserify: {
        dev: {
            options: {
                borwserifyOptions: {
                    debug: true
                }
            },
            files: [
                {
                    expand: true,
                    cwd: "html/assets/js/src",
                    src: "*.js",
                    dest: "html/assets/js"
                }
            ]
        }
    },

    watch: {
        js: {
            files: ["html/assets/js/src/**/*.js"],
            tasks: ["browserify:dev"]
        }
    }

});
  • html/assets/js/src/*.js をエントリーファイルとして、同名ファイルを html/assets/js/ に書き出します
  • html/assets/js/src/**/*.js が変更されると、browserify タスクが走ってコンパイルを行います
  • browserify に渡すオプションは、browserifyOptions に記述します。 debug を true にするとソースマップを生成する様になります。

準備が整ったら watch してみましょう。

$ grunt watch

無事動きましたか?

まとめ

モジュールバンドラーは昨年(2015年)あたりから流行りだして、 使ってみたところあまりにも効率が良く、今ではもう手放せない存在になりました。 CSSプリプロセッサ、タスクランナーと並んで、近年の素晴らしすぎる発明の一つですね。

コメント

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です


*