webpackで混乱したので、簡単な構成でJS、CSS、IMGをバンドル化してみる

# ゴール
JS、CSS、IMGをバンドル化し、エントリHTMLからはバンドルJSを読み込み期待した結果となること。

## webpackとは
webpackはモジュールバンドラの一つで、javascriptアプリケーションにおける依存関係を解決し、  
バンドル化することで静的なアセットを生成する。(と、[公式](https://webpack.github.io/)のイメージから読み取った)

触ってみないと何とも言えないので、とりあえず触ってみる。

## webpackのインストール
npmでインストール出来る。  
グローバルインストールでも良いが、プロジェクト毎に取り扱うことも可能。

※パッケージ管理にYarnを使っているが、npmでも大差無いので適当に読み替える。

```
$ yarn add webpack --dev

success Saved 197 new dependencies.
(省略)
├─ webpack@2.2.1
(省略)
```

`/node_modules/`に`webpack`がインストールされていることが確認出来る。

## 構成を考える
適当に以下の構成にした。  
JS、CSS、IMGをバンドル化してみる。

```:ディレクトリ構成
root
│  package.json
│  yarn.lock
│  index.html
│
├─app
│  │  
│  ├─css
│  │      hello_webpack.css
│  │      
│  ├─img
│  │      img_0.gif
│  │      
│  └─js
│          app.js
│          hello.js
│          webpack.js
│          
└─node_modules

```

## 各モジュールのコードを書く
以下のそれぞれのコードを記述し、`index.html`をブラウザで起動すれば、当然色付けされた`Hello, Webpack!!`と、イメージ画像が表示される。  
下記の例では、`index.html`が、`app.js`、`hello.js`、`webpack.js`、`hello_webpack.css`、`img_0.gif`を読み込む形になっている。

```html:index.html  
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Hello, Webpack!!</title>

  <!-- style -->
  <link rel="stylesheet" href="./app/css/hello_webpack.css">

  <!-- script -->
  <script src="./app/js/hello.js"></script>
  <script src="./app/js/webpack.js"></script>
  <script src="./app/js/app.js"></script>
</head>
<body>
</body>
</html>
```

```css:app/css/hello_webpack.css  
p {
  font-family: sans-serif;
  font-size: 24px;
  font-weight: bold;
  text-shadow:3px 3px 2px #0000ff;
}

img {
  width: 300px;
  height: 300px;
}

img:hover {
  width: 350px;
  height: 350px;
}
```

```javascript:app/js/app.js  
/** 'Hello, Webpack!!'を出力する */
window.onload = function () {
  // Hello, Webpack!!
  var elm_p = document.createElement('p');
  elm_p.textContent = HELLO + WEBPACK;

  // image
  var elm_i = document.createElement('img');
  elm_i.setAttribute('src', './img/img_0.gif');

  // bodyに追加
  document.body.appendChild(elm_p);
  document.body.appendChild(elm_i);
};
```

```javascript:app/js/hello.js  
/** Hello */
var HELLO = 'Hello, ';
```

```javascript:app/js/webpack.js  
/** Webpack!! */
var WEBPACK = 'Webpack!!';
```

上記はHTMLが各種JS、CSSを読み込む、通常のアプリとなる。  
ここから、webpackを用いて依存関係を含めてバンドル化したJSにぶち込む用に記述に修正する。

## 必要なパッケージのインストール
JSのみであれば、追加のパッケージは不要だが、CSS、IMGをバンドル化する為には、別途パッケージのインストールが必要となる。
以下のパッケージをインストールする。

    $ yarn add style-loader css-loader url-loader

- css-loader (style-loader)  
cssファイルを読み込んで、HEADに埋め込む。  
`sass-loader`をチェーンすることで、sass(scss)形式のコンパイルも可能。  
また、`ExtractTextWebpackPlugin`を介することで、cssファイルそのものの出力もOK。

- url-loader
イメージ等のファイルを読み込む。  
ファイルサイズが上限以下の場合はdata-uriで読込み、上限以上の場合はfile-loaderとして読み込む。

## webpack.config.jsの作成
`webpack`コマンドにパラメータをくっつけることでコンパイル可能だが、長ったらしいコマンドを毎回打ち込むのも面倒。  
webpackではコンフィグファイルを用意し、それを元に処理することが出来る。

*※2017/11/3 outputの括弧が閉じられていない点ご指摘頂いたので修正。*

```javascript:webpack.config.js
var path = require('path');

module.exports = {
  // エントリーポイントを指定
  entry : './app/js/app.js',
  // バンドルファイルの出力先を指定
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js',
  },
  module: {
    loaders: [
      // CSSを読み込むローダー
      {
        test: /\.css$/,
        loaders: ['style-loader', 'css-loader'],   // `-loader`は省略可能
      },
      // ファイルを読み込むローダー
      {
        test: /\.(jpg|png|gif)$/,
        loader: ['url-loader'],
      },
    ],
  },
};
```

### index.HTML
バンドル化したjsを`bundle.js`という名前でdistフォルダに生成予定なので、生成したjsを読み込む様に修正する。  
cssの読込みも削除しておく。

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Hello, Webpack!!</title>
</head>
<body>
  <script type="text/javascript" src="./dist/bundle.js" charset="utf-8"></script>
</body>
</html>
```

### app.js、hello.js、webpack.js
`hello.js`、`webpack.js`の変数をそれぞれexportし、`app.js`でrequireする様に修正する。  
`app.js`は、`hello.js`、`webpack.js`に依存している。

```javascript:app.js
var HELLO = require('./hello');
var WEBPACK = require('./webpack');

var css = require('../css/hello_webpack.css');
var img = require('../img/img_0.gif');

/** 'Hello, Webpack!!'を出力する */
window.onload = function () {
  // Hello, Webpack!
  var elm_p = document.createElement('p');
  elm_p.textContent = HELLO + WEBPACK;

  // image
  var elm_i = document.createElement('img');
  elm_i.setAttribute('src', img);

  // bodyに追加
  document.body.appendChild(elm_p);
  document.body.appendChild(elm_i);
};
```

```javascript:hello.js
/** Hello */
var HELLO = 'Hello, ';

module.exports = HELLO;
```

```javascript:webpack.js
/** Webpack!! */
var WEBPACK = 'Webpack!!';

module.exports = WEBPACK;
```

## webpackでコンパイル
`webpack`コマンドを実行することで、`webpack.config.js`を勝手に読込み、コンパイルされる。

```
$ yarn webpack
Hash: a60ff14c7c893864ee0b
Version: webpack 2.2.1
Time: 751ms
    Asset    Size  Chunks                    Chunk Names
bundle.js  337 kB       0  [emitted]  [big]  main
   [0] ./app/js/hello.js 65 bytes {0} [built]
   [1] ./app/js/webpack.js 75 bytes {0} [built]
   [2] ./app/css/hello_webpack.css 943 bytes {0} [built]
   [3] ./app/img/img_0.gif 324 kB {0} [built]
   [4] ./~/css-loader!./app/css/hello_webpack.css 405 bytes {0} [built]
   [5] ./~/css-loader/lib/css-base.js 1.51 kB {0} [built]
   [6] ./~/style-loader/addStyles.js 7.15 kB {0} [built]
   [7] ./app/js/app.js 521 bytes {0} [built]
Done in 1.52s.
```

コンパイル後、`index.html`を叩くと。。。  

***どーーーん***
![webpack-test-20170218_1.PNG](https://qiita-image-store.s3.amazonaws.com/0/163677/ee6473e2-7c86-5d0b-aae0-3ceba9ebc966.png)

# まとめ
この程度の構成であれば、webpackの恩恵はそこまでではないが、  
大規模になり、複数のモジュールが複雑に依存し合う状況において、力を発揮するのではないだろうか。  
規模に従いバンドルファイルのサイズが大きくなることを懸念したが、  
複数に分割することも可能なので、プロジェクトに応じて柔軟なビルドが行えると思われる。

loaderとplugin回りが複雑だが、キモの部分となるので、  
色々と試してみる必要はありそうだ。

いずれ、Babelと連携したES2015のトランスパイルや、  
開発環境用のHMRの設定回りを見ていきたい。