読者です 読者をやめる 読者になる 読者になる

彼女からは、おいちゃんと呼ばれています

ウェブ技術や日々考えたことなどを綴っていきます

Webpack の本質とそれがよく分かるチュートリアル

DHH さんが Rails 5.1 に Webpack を取り入れる意向 を示し、webpacker gem なるものをつくり始めたようである。

今後ますます Webpack は利用されていくであろうが、一方で Webpack はいろんなことができ過ぎるが故にかえって本質が掴みにくい点があると思う。そういえばこの間も、同僚のデザイナーに Webpack がよく分からないんですけど?と質問されていたのだった。

そこで今回 Webpack の本質つまり Webpack がどんな問題を解決しようとしているものなのかについて整理する。また Webpack の中核となる機能をよく理解できるチュートリアルを見つけたので紹介する。

フロントエンドエンジニア Advent Calendar 2016 - Qiita の 9日目が空いていたので、後付けだけどそこに登録した)

Webpack がつくられた背景

Webpack がつくられた背景は公式ドキュメントの motivation ページに書かれていて、ざっくり意訳するとこんな感じ。

  • ウェブサイトが進化してウェブアプリケーションに進化した結果、クライアントサイド(JavaScript)のコードが大きくなったよね。そして、その大きなコードはモジュールの集まりとして構成されるよね
  • モジュールがブラウザでも利用できるように転送されるけど、このとき 2つの極端なやり方があるよね
    • 一方は 1リクエストにつき、1モジュールを返すやり方。これは必要なモジュールのみ転送されるけど、リクエスト数が多くてオーバーヘッドが生じるよね
    • 他方は 1リクエストで全てのモジュールを返すやり方。これはリクエスト数が少ないからオーバーヘッドは小さいけど、(まだ)必要とされていないモジュールまで転送されるよね
  • 両極端の間にあるもっと柔軟なやり方が良いんじゃないかなあ。つまり、全てのモジュールをコンパイルするときに(ひと塊りの巨大なファイルをひとつ作るのではなくて)いくつかの塊(チャンク)をつくる
  • チャンクは必要になるまでロードされないようにする
  • そうすれば初期ロードがスピードアップできるよね
  • あと、スタイルシートや画像、ウェブフォント、HTML テンプレートなど、JavaScript 以外にもリソースはあるんだから、全部モジュールで扱っちゃえば?

Webpack の本質

どうして Webpack をつくったか?についてはまた、公式ドキュメントの Why another module bundler? という項目に明記されている。

Existing module bundlers are not well suited for big projects (big single page applications). The most pressing reason for developing another module bundler was Code Splitting and that static assets should fit seamlessly together through modularization.

ざっくり訳すと(特に後半は意訳になるが)

既存のモジュールバンドラは大きなプロジェクト(大きなシングルページアプリケーション)に適していない。新しく別のモジュールバンドラをつくった最大の理由は、コード分割と、静的なアセットもモジュールシステムで扱えるようにすることである

ここまでのまとめになるが、つまり、Webpack の本質とは次の 2点である。

  • (1) コードを(巨大なひと塊のファイルにするのではなくて)複数の塊(チャンク)に分割し、それらを必要になってからロードするようにすることで、初期ロードにかかる時間を短くすること
  • (2) すべての静的なアセット(スタイルシートや画像、ウェブフォント、HTML テンプレート)もモジュールとして扱えるようにすること

よく分かるチュートリアル

公式のチュートリアルをやってもいまいち本質的なところは掴めなかったが、これをやれば Webpack の本質がよく分かる、というチュートリアルlist of tutorials の中にあったので紹介する。

しかも、既に日本語訳もされていた。

例えば、前述した本質のひとつ目の「コード分割」について、チャンクに分割して、コンテキストに応じてロードする例は下記のような感じ(a タグあるときに button.js チャンクをロードする)

if (document.querySelectorAll('a').length) {
  require.ensure([], () => {
    const Button = require('./Components/Button').default;
    const button = new Button('google.com');

    button.render('a');
  }, 'button');
}

一部そのままでは動かない箇所があったので、そこは コメント欄 を見ながら進めると良いと思う。ソース もある。

参考サイト