Webpack Level 1: 裸のWebpackのデフォルト動作を理解する

## コンテンツ
- [Webpack Level 1: 裸のWebpackのデフォルト動作を理解する](https://qiita.com/takano-h/items/3240181a83ef5e6f26a6)
- [Webpack Level 2: 設定ファイルをカスタマイズする](https://qiita.com/takano-h/items/a0c6783c79fa38c50673)
- [Webpack Level 3: loadersを追加して.js以外のファイルを結合する](https://qiita.com/takano-h/items/1a6a5a0b9d25a677f7d2)
- [Webpack Level 4: Webpack Pluginsを用いて静的サイトをS3に自動デプロイ](https://qiita.com/takano-h/items/abd3fca1514767468878) <- 今ここ

こんにちは！高野です。
さて僕はRailsエンジニアなのですが、実は今までwebpackが怖くてたまりませんでした。フロントエンド開発の際は `create-react-app` や `vue-cli` 、 `angular-cli` などを用いて自動生成される `webpack.config.js` を使うだけで、設定をいじろうなどと大それたことはしないよう気をつけていたのですが、そろそろ自分で設定ファイルを書けるようになりたいなと思い、色々と実験してみた備忘録を公開します。
僕と同じようにサーバーサイドを主戦場とする方や、webpackが何となく怖いという方、ぜひ一緒に勉強していきましょう。

## webpackが理解しづらい理由
webpackは元々、モジュールバンドラと言って、複数のJSファイルを結合して1つのファイルにするためのツールです。Webページを読み込むに当たって、大量のJSファイルを読み込むより1回で全て読み込めた方が素早くページをロードできる、大規模な処理を複数ファイルに分けて書くことで機能単位に分割しやすくなるなどのメリットがあります。
が、webpackはプラグインを追加していくことで、当初のモジュールバンドラとしての役割だけでなく、開発時の様々な便利機能を提供することができるようになっています。例えば開発時にファイルの更新を検知して自動でブラウザをリロードしたり、変更点だけブラウザに反映してくれたりとかいったものが代表的ですね。
このように <b>デフォルトの動作と設定ファイルによる動作、プラグインの提供機能などが多くありすぎて、ごちゃごちゃになってしまうことがwebpackの理解しづらさなのではないか</b> と僕は考えました。そこで、今回は必要最低限の設定で、裸のwebpackがどのように動作するのかについて調べてみたいと思います。

## 環境
- webpack 4.16.3
- webpack-cli 3.1.0
- webpack-dev-server 3.1.5

## 事前準備
- プロジェクトを作成

```bash
$ mkdir webpack-level-1
$ cd webpack-level-1
$ npm init
```

- webpackをインストール

```bash
$ npm install --save-dev webpack webpack-cli webpack-dev-server
```

- 空の config ファイルを作成

```js:webpack.config.js
module.exports = {};
```

- バンドル対象のJSファイルを作成

```js:src/index.js
console.log('Hello, webpack.');
```

## 実験: 裸の webpack を実行する
上記の状態で `webpack` を実行します。

```bash
$ webpack
Hash: b4b9534f490890150ac2
Version: webpack 4.16.3
Time: 85ms
Built at: 2018-07-31 08:42:39
  Asset       Size  Chunks             Chunk Names
main.js  960 bytes       0  [emitted]  main
Entrypoint main = main.js
[0] ./src/index.js 32 bytes {0} [built]

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
```

バンドルできたようです。ここまでで下記のことがわかります。

- `src/index.js` をデフォルトのエントリーポイントとしてバンドルを開始する
- プロジェクトルートの `webpack.config.js` ファイルを設定ファイルとして読み込む
- `dist/` 配下に main.js というJSファイルを吐き出す

なお生成された main.js の中身を見てみると、我々が書いた `console.log('Hello, webpack.')` だけでなく、1000文字くらいのスクリプトになっていることが分かります。何をしているのかはよく分かりませんが、ただファイルを結合するだけでなく何かしら便利なことをしてくれているようです。

## 実験: 裸の webpack-dev-server を実行する
次に `webpack-dev-server` を使ってみましょう。 `webpack-dev-server` は開発時に便利な様々な機能を提供してくれますが、何も設定しない状態では「バンドルしたファイルをlocalhostからサーブする」「ファイルの更新を検知してバンドルし直す」というところまでやってくれます。
まずは必要なHTMLファイルを下記のように作ってみます。

```html:index.html
<!DOCTYPE html>
<html>
<head>
  <title></title>
</head>
<body>
  <script type="text/javascript" src="./main.js"></script>
</body>
</html>
```

この状態で `webpack-dev-server` を実行してみます。

```bash
$ webpack-dev-server
ℹ ｢wds｣: Project is running at http://localhost:8080/
ℹ ｢wds｣: webpack output is served from /
⚠ ｢wdm｣: Hash: baee4eedcd5fedadd755
Version: webpack 4.16.3
Time: 404ms
Built at: 2018-07-31 09:11:24
  Asset     Size  Chunks             Chunk Names
main.js  139 KiB       0  [emitted]  main
Entrypoint main = main.js
 [2] multi (webpack)-dev-server/client?http://localhost:8080 ./src 40 bytes {0} [built]
 [3] (webpack)-dev-server/client?http://localhost:8080 7.78 KiB {0} [built]
 [4] ./node_modules/url/url.js 22.8 KiB {0} [built]
 [7] ./node_modules/url/util.js 314 bytes {0} [built]
 [8] ./node_modules/querystring-es3/index.js 127 bytes {0} [built]
[11] (webpack)-dev-server/node_modules/strip-ansi/index.js 161 bytes {0} [built]
[12] (webpack)-dev-server/node_modules/ansi-regex/index.js 135 bytes {0} [built]
[13] ./node_modules/loglevel/lib/loglevel.js 7.68 KiB {0} [built]
[14] (webpack)-dev-server/client/socket.js 1.05 KiB {0} [built]
[15] ./node_modules/sockjs-client/dist/sockjs.js 177 KiB {0} [built]
[16] (webpack)-dev-server/client/overlay.js 3.58 KiB {0} [built]
[18] ./node_modules/html-entities/index.js 231 bytes {0} [built]
[21] (webpack)/hot sync nonrecursive ^\.\/log$ 170 bytes {0} [built]
[23] (webpack)/hot/emitter.js 75 bytes {0} [built]
[25] ./src/index.js 33 bytes {0} [built]
    + 11 hidden modules
```

実行したままの状態で `http://localhost:8080` にブラウザからアクセスしてみると、先ほど作った空のHTMLファイルが返されます。
Developer ToolのNetworkを見てみると、バンドルされた結果が `http://localhost:8080/main.js` からサーブされていることが分かります。

![localhost_8080.png](https://qiita-image-store.s3.amazonaws.com/0/131942/6254686b-98d4-140b-e683-5c06408a3907.png)

そのレスポンスの中身を見てみると、5,200行くらいのJSファイルになっていることからも分かるように、バンドルした結果だけでなく、ブラウザをコントロールするための websocket の処理などが追加されているようです。

さらに `src/index.js` を更新すると、自動でバンドルし直してくれます。
以上をまとめると、裸の `webpack-dev-server` のデフォルト動作は下記になります。

- プロジェクトルートの index.html を localhost:8080 からサーブする
- バンドルした結果を含むJSファイルを localhost:8080/main.js からサーブする
- ファイルの変更を検知すると自動でバンドルし直してくれる

ちなみに、 `webpack-dev-server` を実行した状態で別プロセスでまた `webpack-dev-server` を実行すると、 `localhost:8081` でサーバーが起動します。ポートが使われていることを検知すると自動でポートを変更してくれるみたいです。
他にも色々ありそうですが、裸の webpack-dev-server を理解するなら一旦ここまでで大丈夫でしょう。

## 実験: 設定ファイルに最低限の情報を書き込む
`webpack.config.js` を下記のように変更してみます。

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

module.exports = {
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, './build'),
    filename: 'bundle.js',
  },
};
```

`path.resolve(__dirname, './build')` というのは、 `webpack.config.js` から見た相対パス `./build` を絶対パスに直してくれる関数です。 `output.path` は絶対パスでないとエラーが出るのでこういう書き方になっています。
エントリーポイントを変更したので index.js を app.js に変更してから `webpack` を実行してみます。

```bash
$ mv src/index.js src/app.js
$ webpack
Hash: 243ed1b1b7f9b93c9dc0
Version: webpack 4.16.3
Time: 86ms
Built at: 2018-07-31 08:58:37
    Asset       Size  Chunks             Chunk Names
bundle.js  960 bytes       0  [emitted]  main
Entrypoint main = bundle.js
[0] ./src/app.js 32 bytes {0} [built]

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
```

すると今度は src/app.js がバンドル対象として読み込まれ、 /build 配下に bundle.js という名前でバンドル結果が吐き出されました。
このようにエントリーポイントと結果ファイルの生成場所を指定することができます。

## まとめ
webpack-dev-server は開発時にlocalhostからファイルをサーブしたい時に使います。
本番環境にファイルをアップロードしたい時は、 webpack を使って本番用のファイルを生成します。
このような使い分けを前提に、設定ファイルやプラグインを使って、目的や要件に応じた開発環境を作っていくのがwebpackなのです。

以上が「裸のwebpackのデフォルト動作を理解する」でした。デフォルト動作を理解できると、なんだかwebpackと心を通わせることができそうな期待感が湧いてきますね。

次回 [Webpack Level 2: 設定ファイルをカスタマイズする](https://qiita.com/takano-h/items/a0c6783c79fa38c50673) では、 `webpack.config.js` をカスタマイズしてみる実験をしています。是非ご覧ください。

## 参考
今回利用したソースコードは [こちら](https://github.com/midwhite/webpack-level-up/tree/level-1) です。