webpack

webpack.config.js 最小設定

巷で webpack 難しいという声が大きいですが、個人的に初期設定でハマるものではないと思っていて(複雑なローダーを複雑に使うとハマる)、勘所掴めるような小さい例を紹介します。

bundle only

src/index.js => public/bundle.js にコンパイルするだけ。babel を使わない。

npm install webpack webpack-cli --save-dev
webpack.config.js
module.exports = {
  mode: process.env.NODE_ENV || "development",
  entry: ["./src/index.js"],
  output: {
    filename: "bundle.js",
    path: __dirname + "/public"
  }
};
## build for dev
npm run webpack

## watch for dev
npm run webpack -w

## build for prod
NODE_ENV=production npm run webpack

-w はなかなか便利で、ファイルに変更があるたびに自動ビルドします。依存ファイルの依存グラフを構成しつつオンメモリに持ってるので、再ビルドが高速です。どれぐらい高速化というと 8秒が0.2秒になったりします。

bundle with babel

npm install webpack webpack-cli webpack-serve --save-dev
npm install babel-loader@^8.0.0-beta @babel/core @babel/preset-env --save-dev
echo '{ "presets": ["@babel/preset-env"] }' > .babelrc
webpack.config.js
module.exports = {
  mode: process.env.NODE_ENV || "development",
  entry: ["./src/index.js"],
  output: {
    filename: "bundle.js",
    path: __dirname + "/public"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: "babel-loader",
        exclude: /node_modules/
      }
    ]
  }
};

コマンドは同じなので略

with html serve

webpack-serve と html-webpack-plugin を使って、 bundle した js を読み込む index.html を生成しつつ localhost にサーバーを立てます。

注意点として webpack-serve は --watch と同様にサーバー内のインメモリにビルドしたファイルを構成し、それを返却するのでファイルIOがなく速いのですが、public 以下にファイルを出力しません。

yarn add webpack webpack-cli webpack-serve html-webpack-plugin -D
yarn add babel-loader@^8.0.0-beta @babel/core @babel/preset-env -D
echo '{ "presets": ["@babel/preset-env"] }' > .babelrc
webpack.config.js
const HtmlPlugin = require("html-webpack-plugin");
module.exports = {
  mode: process.env.NODE_ENV || "development",
  entry: ["./src/index.js"],
  output: {
    filename: "bundle.js",
    path: __dirname + "/public"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: "babel-loader",
        exclude: /node_modules/
      }
    ]
  },
  plugins: [new HtmlPlugin()]
};
npm run webpack-serve --port 4000

デプロイする際は同様に npm run webpack して生成物を配布します。

任意な html を初期状態にしたい場合は new HtmlPlugin({ template: 'src/index.html'}) のように自分で指定した HTML を指定します。

これがいわゆるSPAのスタートラインです。

未来へ向けて

最近のフロントエンドでwebpackの乱用がすぎるのはそうだと思っていて、とはいえ利用者がアレもコレもやろうとして、勝手にハマってるだけなところが大きいと思います。

例えば代表的なのが css-modules をやろうとすると複数の loader を複雑に使って実現していて、webpack 2系から 3系に上げられなくて爆死、という例が多いですね。単体で難しいわけじゃないですが、loader 組み合わせで爆発するのもwebpackの特徴です。

webpack はES Modules が最適化される前の必要悪であって、IEが死んだ未来の時点でサッと webpack を抜いてもそのまま動くようなコードが理想です。なので require 使わず ESM の import/export だけ使うみたいな感じにがたぶん痛くない設計です。

とはいえ ES Modules の静的解析を行って読み込みを最適化するのが実装されるのはだいぶ先の未来だと思われます。たとえば nginx が JS の依存の静的解析が行ってhttp/2のストリームに最適にパケットを詰める、みたいな未来がこなくてはなりません。アセットサーバーが依存解析して先読みのグラフを作る仕様が必要です。

少なくても現時点から5年先ぐらいは 一つのファイルに bundle するのが最適解だと思われます。もしかしたら10年さきもやってるかもしれません。ないといいですね。