徒然技術日記

Object.prototype.__noSuchMethod__

vendor.js の終焉と Granular Chunks

webpack を使った code splitting のベストプラクティスとして,v3 以前の CommonsChunkPlugin の時代から node_modules 以下に置かれている依存ライブラリを vendor.js という単一の chunk にまとめる方法が紹介されていました.

これは webpack の公式ドキュメント Caching | webpackGoogleMake use of long-term caching  |  Web Fundamentals  |  Google Developers でも説明されている通り,「ライブラリのコードはアプリケーションコードに比べると更新頻度が低い」という仮定のもと vendor.js にライブラリコードを切り出すことで,キャッシュ効率をよくすることが主な目的でした.

しかし時は流れ,いつしか「ライブラリのコードは vendor.js に切り出すもの」という習慣だけが根付き,特に理由を考えずに盲目的に実施するものになってしまったように思います(少なくとも私はそうでした.webpack v4 から導入された SplitChunksPlugin の Examples には全てを vendor.js に固めるのはやめた方がよいと書かれているのできちんとドキュメント読みましょうという話ではあるのですが...).SplitChunksPlugin のデフォルト設定が vendor.js を生成するようになっていることもそうした習慣を後押ししたかもしれません.

その間にパッケージの更新を取り巻く状況は大きく変化しました.Greenkeeper,Renovate,Dependabot といった依存パッケージの更新を Pull Request の形で通知してくれるサービスが広まり,場合によっては automerge 機能で半自動的なパッケージ更新まで行えるようになったことで,ここ数年で毎週や毎日などの高い頻度でパッケージを更新する習慣が急速に普及しました.

このような環境では,前述した「ライブラリのコードはアプリケーションコードに比べると更新頻度が低い」という仮定は全く成り立たなくなります.スプリントごとにリリースしている場合にはほぼ確実にリリースごとに vendor.js のキャッシュが無効化されることになりますし,Continuous Deployment で一日に何度もリリースしているプロダクトでもアプリケーションコードと同等以上にライブラリが更新されるでしょう.

vendor.js の利点であったキャッシュ効率のよさがなくなってしまうと,今度はその欠点が目立つようになってしまいます.それは「特定のページでしか使われていないライブラリも vendor.js に固められることで,すべてのページで読み込まれるようになってしまう」という点です.せっかく routes ごとにアプリケーションコードを分割していても,依存ライブラリが同じところに固まっていては片落ちといえるでしょう.

この問題を解決する一つの方法が,Next.js 9.2 で導入された Granular Chunks と呼ばれる設定です.これは GoogleChrome チームによる研究に基づいたより効率的な chunk splitting の方法で,以下のような戦略からなります:

  • 160 KB 以上の依存パッケージは個別の chunk に分ける
  • react, react-dom などのフレームワーク(≠ライブラリ)を一つの chunk にする
  • そのほかの共通モジュールを必要な分だけ切り出す(デフォルトよりもかなり多くの chunk を生成する設定になっている)

実装のより細かい部分や誕生の背景などを含めた詳細については Improved Next.js and Gatsby page load performance with granular chunking を参照いただきたいのですが,これだけでも Granular Chunks が依存ライブラリを過度に特別視することなく可能な限り chunks を分割することで,各 route において必要最小限のコードを読み込むようにしようとしていることがわかるかと思います.

まとめ

  • 依存パッケージを高頻度に更新しているプロジェクトにおいては,vendor.js に依存パッケージを固めるのは却って悪手となる
  • vendor.js のように「一つの chunk に共通モジュールを全てまとめる」という戦略は非効率であり,Granular Chunks はそれに対する一つの解である
    • しかも Google Chrome チームの研究という裏付け付き