ざっくりWebpack入門 Vol.1

こんにちは。TECH DRIVEの小笠原です。
今回は、昨今話題のWebpackについて、自分が学習時に苦労した点などを踏まえ、ご紹介したいと思います。

Vol1の本記事ではWebpackを使ってJavaScriptファイルのモジュール化と依存関係を管理する方法をご紹介します。

Webpackって何者?

モジュールバンドラーと言われるものらしいです。 モジュールをバンドルする(束ねる)ツール。
Webpackにおけるモジュールとは、Webサイトを構成するおおよそ全てのファイル(JSはもちろんHTML/CSS/画像ファイルまで)を指します。

f:id:kabaneshi:20170214155041p:plain

何やら壮大なことができそうですが、壮大すぎてよくわからない。
「全てがモジュール?」「画像ファイルもモジュールなの?」等、Webpackに出会った当初この辺りの感覚が中々掴めずに頭を悩ませました。

この辺りの詳細を含めると話が複雑になるので、本連載では少し話を小さくして、毎回Webpackで行える一部の機能をピックアップしながら、少しずつご紹介していきたいと思います。

本記事でやること

冒頭でも触れた通り、Vol1の今回はWebpackでJavaScriptファイルのモジュール化と依存関係を管理する方法について触れていきます。
環境はnpmが導入済みのMac OSを想定しておりますので、予めご了承くださいませ。

モジュール化について

Webpackの話に入る前に少しモジュールについても触れておきます。
複数の処理が無秩序に書かれたコードは可読性が悪かったり、バグの温床になりがちです。
解決策の一つとして、コードを機能単位で分割(部品化)し、名前をつけて管理する方法があります。
上記を実現するための方法の一つがモジュール化です。 (少々乱暴な言い方ですが)

モジュール化を行うメリットとしては、以下のようなことがあります。

  • どこに何が書いてあるのかが把握しやすい
  • 一度書いたコードを再利用しやすい

JSではモジュールをどう実装すべきかを定義したガイドブックのようなものが複数存在します。(CommonJSやAMDなどがこれに当たります)
しかし、現状ブラウザで動作するJSではガイドブックのルールに従い実装されたモジュールを取り扱う機能がないため、Webpack等のツールを導入しこれを実現します。

依存関係の管理について

JSファイルの依存関係についても少し触れておきます。

依存関係の考慮が必要な例として、jQuery等の外部ライブラリの利用が挙げられます。
当然ですが、jQueryの機能を使用して書かれたコードは、jQuery本体のファイルが先に読み込まれている状態でなければ動作しません。
依存関係とはこのように「あるファイルに書かれたコード」が「別のファイルのコード」に依存している状態を指します。

依存関係は、大抵以下のようにHTMLに記載するscriptタグの順番で管理を行います。

<!DOCTYPE html>
<html lang="ja">
<head>
   <meta charset="UTF-8">
   <title>webpack sample</title>

   <script src="./js/common/hoge.js"></script>
   <script src="./js/common/huge.js"></script>
   <script src="./js/main.js"></script>
</head>

scriptタグはasync等の属性を指定しないかぎり、頭から順番に実行が行われ、先頭の処理が完了するまで次のファイルが実行されることはありません。
そのためjQuery等の「依存されるファイル」は頭の方に書きます。

しかし、依存関係の考慮が必要なファイルが増えてきたりHTMLファイルが多い場合等は、scriptタグでの依存関係管理は煩わしさがあります。
以下では、この煩わしさを解消する方法をご紹介します。

Webpackの導入

前置きが長くなってしまいましたが、ここからは実際のコードを用いて説明をしていきたいと思います。
今回はプロダクトが以下のディレクトリ構造であることを前提に進めます。

├── dist  // HTMLから呼び出されるJSファイル   
├── index.html  
├── src  
   └── js // モジュール化されたJSファイル群  
       ├── entry.js  
       ├── main.js  
       └── sub.js  

まずは、ディレクトリ直下で以下のコマンドを実行し、package.jsonファイルを作成しましょう。

npm init

いくつかの質問が続いた後に、package.jsonが作成されていれば成功です。 つづけてWebpackのインストールを行います。

npm install --save-dev webpack

これでWebpackを利用するための準備ができました。

必要なファイルの準備

次にモジュールファイルの準備を行っていきます。
今回は上記で少し触れたCommonJSのルールに従いモジュールの定義や呼び出しを行います。
まずはsrc/js直下に以下の2つのファイルを用意します。

  • src/js/main.js
    main.jsの中身は実行時にアラートダイアログを表示するだけのシンプルな処理です。 module.exportsに代入をしている関数がモジュールとなります。
module.exports = function() {
  alert('Hello world');
}
  • src/js/sub.js
    sub.jeの処理は実行から2秒後にアラートダイアログを表示します。
module.exports = function() {
   setTimeout(function(){ alert('Hello webpack'); }, 2000);
}

次に作成したモジュールを呼び出し、実行するためのファイルを作成します。
CommonJSではrequireという仕組みを通し、exportsに追加したモジュールにアクセスを行いますので、そのように書きます。
src/js/entry.js

// モジュールファイルの呼び出し
var main = require("./main"),
    sub = require("./sub");

// モジュールの実行
main();
sub();

先ほど、依存関係について触れましたが、ここではentry.js内のコードが依存する処理をrequireを使って呼び出しています。
呼び出し元や呼び出し方が違うだけで、これはHTMLから外部のJSファイルを呼び出すのとよく似ています。
Webpackを使用することで、このようにJSファイル内で依存関係を管理できるようになります。

Webpackで使用するファイルについて

ここで、これまで作成をしてきたファイルについて少し整理をしてみたいと思います。

f:id:kabaneshi:20170214173314p:plain

  • 1. モジュール郡
    機能単位で分割されたファイル。
  • 2. エントリーポイント
    分割したモジュールファイルの呼び出しや実行処理を記載するファイル。
    このファイルをビルドすることで、複数のJSファイルが一つに結合される。
  • 3. 実行ファイル
    2のビルド結果。HTMLから呼び出されるファイル。

JSファイルの結合と実行

最後にentry.jsをビルドし、dist直下に出力してみたいと思います。

今回のルートディレクトリ直下で以下のコマンドを実行してください。
webpackコマンドの第一引数にエントリーポイントのファイル情報、第二引数に結合されたファイルの出力情報を渡します。

webpack src/js/entry.js dist/build.js

コマンド実行後、dist直下にbuild.jsが作成されたことを確認してみましょう。

結合ファイルの実行

作成したbuild.jsをHTMLファイルから読み込んだ際に期待した結果になることを確認してみましょう。 ルート直下にindex.htmlファイルを作成します。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Document</title>
</head>
<body>

<script src='./dist/build.js'></script>
</body>
</html>

index.htmlをブラウザで開いた際に、アラートダイアログとして「Hello world」が表示され、それを閉じた2秒後に「Hello webpack」が表示されれば成功です。

設定ファイルの作成

webpackコマンド実行時にいちいち引数でファイル名を渡すのは、面倒かと思います。
これは、設定ファイルを作成することで解決できます。
ルートディレクトリ直下にwebpack.config.jsというファイルを作成し、以下の内容を記載してください。

webpack.config.js

module.exports = {
     entry: './src/js/entry.js', // ビルド対象のファイル情報
     output: {
          filename: "./dist/build.js" // 出力先のファイル情報
     }
};

上記のようにentoryにエントリーポイントのファイル情報、outputに出力情報を記載することでwebpackコマンドのみでファイルの結合/出力が実行できます。

まとめ

いかがでしょうか?本記事を通して少しでもWebpackの輪郭をつかんで頂けたのなら幸いです。

Vol2ではWebpackのloaderという機能を使いES6のトランスパイルをする方法をご紹介したいと思います。

TECH DRIVEについて

TECH DRIVEは「技術者の成長を加速させる」をキーワードに都内で活動をしているコミュニティです。
TwitterやFacebookにて技術ネタやイベント情報の発信を行っていますので、ご興味があれば、いいねやフォローをお願いいたします。