JavaScript
webpack

webpack入門3 ~Output Management~

More than 1 year has passed since last update.

はじめに

引き続きwebpackのチュートリアルをやってみます。今回はOutput Managementということで出力されるbundleファイルの制御と管理の自動化をやってみます。

Preparation

まずはjsファイルを追加します。

src/print.js
export default function printMe() {
  console.log('I get called from print.js!');
}

次にindex.jsを変更します。
buttonを追加してクリックされた時に先ほど追加したprint.jsのprintMe関数を呼び出すようにします。

src/index.js
import _ from 'lodash';
import printMe from './print.js';

function component() {
  var element = document.createElement('div');
  var btn = document.createElement('button');

  element.innerHTML = _.join(['Hello', 'webpack'], ' ');

  btn.innerHTML = 'Click me and check the console!';
  btn.onclick = printMe;

  element.appendChild(btn);

  return element;
}

document.body.appendChild(component());

次はindex.htmlを変更します。
今回はprint.jsとindex.jsをそれぞれbundleするので、読み込むところもそれぞれ読み込むようにします。

dist/index.html
<html>
  <head>
    <title>Output Management</title>
    <script src="./print.bundle.js"></script>
  </head>
  <body>
    <script src="./app.bundle.js"></script>
  </body>
</html>

これで下準備は整いました。
そうしましたらbundleの設定を変更します。

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

module.exports = {
  entry: {
    app: './src/index.js',
    print: './src/print.js'
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

ポイントとしては、entryの要素が二つになっているところと、outputのfilenameが動的になっている点です。
entryは二つのjsファイルをそれぞれbundleするような設定担っており、outputの動的部分([name])は、entryで名前をつけたbunble名を受け取るってそれを出力ファイルに使用しています。

これをbundleしてみます。

$ yarn run build
yarn run v1.0.1
$ webpack
Hash: 3be341c5c31054132c13
Version: webpack 3.6.0
Time: 457ms
          Asset     Size  Chunks                    Chunk Names
  app.bundle.js   545 kB    0, 1  [emitted]  [big]  app
print.bundle.js  2.74 kB       1  [emitted]         print
   [0] ./src/print.js 84 bytes {0} {1} [built]
   [1] ./src/index.js 403 bytes {0} [built]
   [3] (webpack)/buildin/global.js 509 bytes {0} [built]
   [4] (webpack)/buildin/module.js 517 bytes {0} [built]
    + 1 hidden module
✨  Done in 1.17s.

という結果が出力され、dist/以下にbundleされたファイルが追加されていることがわかります。

$ ls ./dist/
app.bundle.js   bundle.js   index.html  print.bundle.js

これで複数ファイルのbundleはできました。
しかしこのままですとentry pointの名前を変更したり追加したりする場合、yarn buildすると出力されるファイル名は新しくなりますが、参照されるbundleファイルは以前のままです。

そこで、HtmlWebpackPluginというpluginを使って解決していきます。

Setting up HtmlWebpackPlugin

まずはpluginをインストールします。

yarn add --dev html-webpack-plugin

インストールしたpluginを使います。

webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: {
    app: './src/index.js',
    print: './src/print.js'
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Output Management'
    })
  ],
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

bundleします。

$ yarn run build
yarn run v1.0.1
$ webpack
Hash: 3b750d173451716be2fa
Version: webpack 3.6.0
Time: 807ms
          Asset       Size  Chunks                    Chunk Names
  app.bundle.js     545 kB    0, 1  [emitted]  [big]  app
print.bundle.js    2.74 kB       1  [emitted]         print
     index.html  254 bytes          [emitted]
   [0] ./src/print.js 84 bytes {0} {1} [built]
   [1] ./src/index.js 403 bytes {0} [built]
   [3] (webpack)/buildin/global.js 509 bytes {0} [built]
   [4] (webpack)/buildin/module.js 517 bytes {0} [built]
    + 1 hidden module
Child html-webpack-plugin for "index.html":
     1 asset
       [2] (webpack)/buildin/global.js 509 bytes {0} [built]
       [3] (webpack)/buildin/module.js 517 bytes {0} [built]
        + 2 hidden modules
✨  Done in 1.69s.

出力された内容見ると追加したpluginが作用していることがわかります。
さらにdist/index.htmlを見てみましょう。

dist/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Output Management</title>
  </head>
  <body>
  <script type="text/javascript" src="app.bundle.js"></script><script type="text/javascript" src="print.bundle.js"></script></body>
</html>

中身が今までと異なっています。
何が起こったかと言いますと、HtmlWebpackPluginがentry pointで指定しているbundleファイルを全て追加した状態の新しいindex.htmlを作成してくれました。これでbuildのたびに毎回自動でentry pointに対応されたhtmlファイルを出力できるようになったので、entry pointを変更しようが追加しようが関係なくなりました。便利ですね。

Cleaning up the /dist folder

distディレクトリにbunldeしたファイルを出力しているわけですが、 このままですとentry pointが変わったりすると使わなくなったりしたゴミファイルがどんどん溜まっていくことになります。そこでゴミファイルが残らないようbuildのたびにdistディレクトリをキレイにしましょう。

そのためにclean-webpack-pluginというpluginを入れます。

$ yarn add --dev clean-webpack-plugin

buildごとにキレイする設定を追加します。

webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// ここを追加
const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
  entry: {
    app: './src/index.js',
    print: './src/print.js'
  },
  plugins: [
    // dist以下をキレイにする設定を追加
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
      title: 'Output Management'
    })
  ],
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

これで準備は完了なのであとはbuildを走らせるだけですが、実行前のdistディレクトリを見ておきます。

$ ls dist/
app.bundle.js   bundle.js   index.html  print.bundle.js

この中でいうとbundle.jsがゴミファイルですね。このファイルが削除されることを期待します。

$ yarn run build
yarn run v1.0.1
$ webpack
clean-webpack-plugin: /xxxx/xxxx/xxxx/webpack-demo/dist has been removed.
Hash: 3b750d173451716be2fa
Version: webpack 3.6.0
Time: 987ms
          Asset       Size  Chunks                    Chunk Names
  app.bundle.js     545 kB    0, 1  [emitted]  [big]  app
print.bundle.js    2.74 kB       1  [emitted]         print
     index.html  254 bytes          [emitted]
   [0] ./src/print.js 84 bytes {0} {1} [built]
   [1] ./src/index.js 403 bytes {0} [built]
   [3] (webpack)/buildin/global.js 509 bytes {0} [built]
   [4] (webpack)/buildin/module.js 517 bytes {0} [built]
    + 1 hidden module
Child html-webpack-plugin for "index.html":
     1 asset
       [2] (webpack)/buildin/global.js 509 bytes {0} [built]
       [3] (webpack)/buildin/module.js 517 bytes {0} [built]
        + 2 hidden modules
✨  Done in 1.91s.

無事成功しました。実際にdistディレクト以下を見てみます。

$ ls dist/
app.bundle.js   index.html  print.bundle.js

期待通りの動きをしていました。
以上になります。

おわりに

bundle結果を手動ではなく自動で管理するようにしたことによってだいぶ本番運用に近づいてきた感がします。しかし、本番で静的ファイルを扱う場合ブラウザキャッシュを考慮する必要があり、このままだと本番運用には少し足りません。digestつきのbundleファイルを出力し、それを管理するためにManifestというものを作ります。

公式のチュートリアルではこの章で簡単に触れられていますが、さらっとしすぎなので別でまとめることとします。