2017.06.05

Webpack 2 をざっとペロる

概要

Webpack は モジュールバンドラーです。そのまま言っちゃえば部品(モジュール)を束ねる(バンドルする)ツールです。Webpackは以下のような特徴があります

  • モジュール間の依存を解決する
  • 複数のモジュールを一つのファイルにバンドルする(ことができる)のでリクエスト数が減る
  • 製造したモジュールの見通しも良くなる(管理が楽になる)
  • CSSや画像もモジュールとして扱うことが出来る

jQueryでの例

例えば以下のような状況を考えます

  • jQueryを使用
  • タイルレイアウトを実現するために Masonry プラグインを使う
  • タイルレイアウトには画像が含まれているので imagesLoaded プラグインも使う
  • これらを扱うために外部ファイルとして app.js を用意

そのためには4つのJavaScriptファイルをHTMLで読み込む必要があります。

この従来の方法は、他の jQuery プラグインが必要になればどんどん読み込むファイルが増えていくことになり、気づいたら js ディレクトリは 大量の jQuery プラグインでぎっしり、みたいなことはよくあることです。言い換えると、app.js は jQuery やそのプラグインに依存している状態で、依存するものが増えれば増えるほど管理が煩雑になってしまいます。

そこで webpack を使えば HTML 上では app.js を1つ読み込むだけで良くなります。依存する機能は全て app.js に含まれ、その元となる機能は npmYarn で簡単に追加することも可能です。幸い、jQuery 、Masonry 、imagesLoaded は npm で公開されているので先の例は実現が可能となっています。

webpackを設定する上での4つのコアコンセプト

webpackを使い始めるにあたり、目的に合わせて設定ファイルを用意します。設定には 4 つのコアコンセプトがあり、それらを理解する必要があります。そのコアコンセプトとは以下のものとなります。

  • entry … モジュールを束ねるための起点を webpack に教える
  • output … 出力先、出力方法を設定
  • loaders … js や css、image 等をモジュールとして扱う時に必要な形式に変換する役目を担う
  • plugins … バンドル生成時に追加のモジュールを用いて成果物に作用させる役目を担う

※ loader は 個々のファイルに作用し、plugin は生成後のファイルに作用すると言えば想像しやすいかも。

簡単な webpack の設定例からコアコンセプトを学ぶ

webpackのデフォルトの設定ファイル名は「webpack.config.js」となっており、以下のような設定をしてみました。

この設定は、Babel を利用しての開発を想定しており、バンドルの生成時に UglifyJSPlugin というプラグインでソースコードを圧縮する例となります。

package.json は以下の通り。

entry

entry は、モジュールを束ねるための起点(エントリーポイント)を webpack に教えます。先の例では以下の用に設定しています

エントリーポイントは

  • 文字列
  • 配列
  • オブジェクト

の三種類で設定する方法が提供されています。バンドルを複数生成する場合はオブジェクトで設定することになるでしょう。

この例ではエントリーポイントをオブジェクトで設定しており、エントリーポイントの「名前」を一意のキーとして “home” と設定しています。値には、エントリーポイントの場所を示すパスが設定してあります。webpack はここに記述されている場所にあるファイルを読みに行き、バンドルします。

では仮に、このエントリーポイントに jQuery を使える状態にしてみます。まず、npm で jquery を追加します。

次に、src ディレクトリの home.js で以下のようにしてみました。

require で jQuery を読み込んでいます。require をすると node_modules の中から jQuery を探し、バンドル生成時に依存を解決してくれるようになります。このJSは想定の通り、Chromeなどのブラウザのコンソールで jQuery のバージョンが表示されます。一応、HTMLも載せておきます

output

バンドルしたファイルを出力する先を具体的に指定するオプションが output です。複数の entry point があったとしても output の設定は一つです。

先程上げた例を以下に示します

output 設定の最小の要件は、出力するファイルの名前を定義する filename オプションと、具体的な出力先を示す path オプションの設定となります。示した例の通り、ひとまずはこの最小の要件にだけ触れておきましょう。

filename オプション

filename オプションは、出力するファイルの名前を定義します。エントリーポイントが、オブジェクトを用いて一意の名前がついている場合 [name] を指定すれば、オブジェクトのキー(チャンク)が出力されるようになります。

今回の例では、以下のようにエントリーポイントは一つだけ home という名前で設定されています。

filename オプションの設定では、[name].js と設定してあるので、出力されるファイル名は home.js となります。

path オプション

path オプションは、ファイルの具体的な出力先を 絶対パス で設定します。今回の例は以下の様に __dirname で webpack.config.js が実行されている場所を指定し、その先の場所を文字列結合で明示しています。

これで出力先は、dist というディレクトリに、home.js という名前で出力されるようになります。

loaders

loaders は webpack.config.js 上では、module というオブジェクトに設定します。この loader を設定するには最低でも以下の2つの設定が必要です。

  1. 具体的にどの形式のファイルを対象とするかを test というオブジェクトに正規表現で指定
  2. どの loader を使用して、モジュールとして扱える形式(.js)に変換するかを use に指定

今回の例では以下のようになっておりました。

この設定は、.js という拡張子を対象とすることを test で指定し、loader として babel-loader を使用することを use に設定しています。exclude では「babel-loader で変換することを除外する対象」を設定しています。webpack 2 からは、module は rules の配列にオブジェクトで列挙するようになっているのでご留意ください。

loader は、gulp や grunt のような「タスク」であり、output や entry の設定を参照しつつ実行されます。主に変換・コンパイルを行ったりします。ここで設定している babel-loader は、babelで書かれている JavaScript を、ブラウザが解釈できる現行の JavaScript に変換します。変換の過程でシンタックスエラーなどがあれば拾ってくれたりもします。

これで、エントリーポイントで設定したファイルに対し、依存関係を解決し、尚且つ node_modules のファイルは loader の対象から除外しつつも、babel-loader を使って .js に変換します。

plugins

loader はファイル毎に変換を行うタスクですが、plugins はバンドルされたモジュールに対して、更なる変換などのなんらかの作用をさせるために扱われるのが一般的だそうです。

このポストで示している設定では、UglifyJSPluginを使用してバンドルされたファイルを圧縮しています。実際に圧縮されたファイルはこんな感じになっています。

圧縮後は500倍近くファイルサイズが小さくなっていることがわかります。

プラグインの簡単な使い方として

  1. npm 経由でプラグインをインストール
  2. webpack.config.js で require する
  3. plugins の配列で new する

殆どのプラグインではオプションで動作をカスタマイズできるそうで、必要に合わせて調べて利用するといいようです。

今回の設定例をもう一度見てみましょう

1行目で、インストールした plugin である、UglifyJS を require することで、plugin を 使用可能な状態にしています。plugins の設定では、配列に new してplugin を適用しています。

まとめ

設定のコアを知るだけでも、すごく便利なことがわかるwebpackですが、webpack の設定はこのポストで紹介した以外にも沢山の設定項目があります。公式の Configurationのページに全ての設定項目が紹介されているので興味が有る方は確認してみるのもいいかもしれません。