sosukesuzuki.dev

2022年 sosukesuzuki 1人アドベントカレンダー

December, 09 2022

Bundle Preloading があれば Module Declarations って要らなくないか?と思ったがそんなこともないらしい

2022年の sosukesuzuki 1人アドベントカレンダー 9 日目です。

5 日目と同じように、ちょっとだけ Web の未来のことを考えます。

Module Declarations とは

Stage 1 のプロポーザルです。前は Module Fragments と呼ばれていました。提案されたタイミングに解説記事を書いたのでよければそちらも参照してください。当時とは構文や細かい挙動がやや異なりますが、根本的に解決したい課題は変わっていません。

https://sosukesuzuki.dev/posts/stage-0-module-fragments/

Module Declarations はモジュール内モジュールの宣言を可能にします。具体的には、次のような構文でモジュール内に新しいモジュールを宣言できます。

module foo {
  export const exported = "Hello, World!";
}

import { exported } from foo;

console.log(exported); // Hello, World!

Module Declaration は主に webpack などのモジュールバンドラーの出力として使われることを想定しています。

Module Declaration は、現在のモジュールバンドラーが抱えるいくつかの問題を解決します。

1 つめの課題は、モジュールバンドラーの実装が複雑になりすぎているということです。

普段多くの Web 開発者が ECMAScript Modules を使ってコードを書いています。そしてそれをモジュールバンドラーを使ってバンドルしています。このモジュールのバンドルという処理は、ネイティブの ECMAScript Modules の振る舞いをエミュレートすることと等しいです。ECMAScript Modules を使ったコードを、ECMAScript Modules を使わない形に変換するわけですから。

そのせいでモジュールバンドラーの実装は複雑になっています。モジュールバンドラーの出力を Module Declarations で表現できればその複雑さをいくらか軽減できます。

2 つめの課題は、モジュールバンドラを使うと ECMAScript Modules のコードがブラウザで実行されるときまで残らないため、JavaScript エンジンによる最適化が効かないということです。

筆者は JavaScript エンジンの実装に詳しくないので ECMAScript Modules を直接実行するときと、それに相当するバンドルされたコードを実行するときを比較して、どの程度パフォーマンスに有意差が出るかは知りません。が、一般的なプログラミング言語処理系に思いを馳せれば、ランタイムにコードが引き渡されるときに、静的に判断できる情報が多ければ多いほど最適化は効きそうな気がします。

Module Declarations はこのような課題を解決しようとしているのです。

モジュールバンドラ使うのやめたらどう?

ならそもそもモジュールバンドラーを使わなければ良いのではないかという発想にもなりますが、モジュールバンドラーを使わずに ECMAScript Modules をブラウザで直接実行するのはパフォーマンスの観点から控えたほうが良い場合が多いです。というのも、ECMAScript Modules では一つのモジュールを import するごとに一つのファイルをフェッチすることになるので、アプリケーションが起動するまでのオーバーヘッドが大きくなりがちなのです。

Bundle Preloading とは

それとは別に、Bundle Preloading と呼ばれる技術が WICG で提案されています。

これは、ウェブサイトを構成するアセットを配信する前に1つのファイルに固めておき、ユーザーはそのウェブサイトを訪れたときに固められた一つのファイルのみをフェッチする、という技術です(固められたアセットの展開はブラウザで行われるため、見かけ上は複数のファイルをフェッチしているように見える)。

5 日にも書きましたが、この技術があればモジュールバンドラーを使わず直接ブラウザで ECMAScript Modules のコードを実行してもモジュールの import に伴うフェッチによるオーバヘッドが発生しなくなることが期待できます。

ECMAScript Modules の観点では、両方とも似た問題を解決する

Bundle Preloading は JavaScript のみを対象とした技術ではなく、CSS ファイル、画像ファイル、WebAssembly ファイルなどウェブサイトを構成するアセット全般に対して適用できる汎用的な Web の機能として提案されています。

とはいえ、ECMAScript Modules の視点から見れば 2 つの技術は同じような課題を解決できます。

現在のモジュールバンドラーには

  • 実装の複雑さ
  • JavaScript エンジンによる最適化の阻害

などの問題があることを述べました。

Bundle Preloading があれば、そもそもモジュールバンドラーを使う必要がなくなる(可能性がある)ため、モジュールバンドラーの実装の複雑さを気にする必要はないし、ECMAScript Modules がそのままブラウザ上で実行されるのでそのための最適化も効きそうです。

つまり Bundle Preloading は Module Declarations が解決したかった課題を解決できるのです。

じゃあより汎用的な Bundle Preloading の方が良いのでは?

筆者はもともと Module Declarations を知っていて(当時はまだ Module Fragments という名前でしたが)、後から Bundle Preloading のことを知りました。

そのときに「えっじゃあより汎用的な Bundle Preloading があれば、Module Declarations って要らないんじゃ?」と思いました。

そんなこともないらしい

しかし、実はそんなこともないらしいのです。というより正確には、「Bundle Preloading があれば」という条件が難しいため Module Declarations もあったほうがよい、という感じでしょうか。

最近 Module Declarations の README を眺めていたら、筆者の疑問にピッタリ回答してくれているセクションを見つけました。

Why have module declarations, rather than just focusing on general-purpose resource bundles?

(Module Declarations の READMEから引用)

詳しくは README を読みにいってほしいですが、雑に解釈すると、

  • Module Declarations はシンプルな仕組みだが、Bundle Preloading の方はそれと比べて複雑である。
  • Module Declarations は JavaScript の最終的な成果物のみに影響を与えるが、 Bundle Preloading はより広い範囲に影響を与えるため、キャッシュやセキュリティ、プライバシーなどの重要な問題についてより慎重な検討が必要になる。

というようなことが書かれています。要は仕様の策定や実装において、低いインベストでモジュールバンドラーの課題を解決できるということなのでしょう。

納得。

おまけ

Subresource loading with Web Bundles も Bundle Preloading と似た技術に見えるけど、どう違うんだろう?単に背景にあるのが Web Bundles ってだけなのかな。詳しい人いたら教えて下さい。

参考リンク