Webページのサブリソースを一つにまとめる Resource bundles (Bundle preloading) とは

Webページのサブリソースを1つにまとめる 「Resource bundles」 という仕組みが検討されている。


[目次]

はじめに

JavaScript, CSS, 画像といったWebページを表示するのに必要なリソースを一つのファイルにまとめるというのは、今でも行われておりwebpack、rollup、Parcel、esbuildといったツールが利用されている。

Resource bundles」の目的を読むと、既存のバンドラーは、個々別にリソースをフェッチするのに比べいくつかのデメリットが有ると述べている。

  • リソース個別のフェッチであれば、MIMEタイプによってはフェッチしながらそのデータの処理を進められるが、それができない
  • リソース個別のフェッチであれば、リソースごとにキャッシュコントロールができる

こういったデメリットを解消しつつ、その他のバンドラーと同様に通信のオーバヘッドを減らしつつ、データの圧縮を行うのが「Resource bundles」 の目的となっている。

2行で説明すると

Resource bundles」 は

  • ファイルを1つにまとめるのではなく、HTTPレスポンスを1つのファイルにまとめる
  • ブラウザによって処理されるので、あたかも個別のHTTPレスポンスを受け取ったかのように処理される

様々な疑問が出てくると思うが、とても長いFAQがあるので見てみるのが良さそう
https://github.com/littledan/resource-bundles/blob/main/faq.md

利用例

利用例を見つつ説明説明していく。
Resource bundles を読み込んで使う例は下記のとおりである

<script type=loadbundle>
{
    "source": "pack.rbn",
    "scope": "static/"
    "paths": {
        "a.js": ["bGpobG", "FzZGZq"],
        "b.js": ["bGpobG", "sbnNkd"],
        "style/page.css": ["a2FzaG"],
    }
}
</script>
<link rel=stylesheet href="static/style/page.css">
<link rel=stylesheet href="static/style/button.css">
<button onclick="import('static/a.js')">a</button>
<button onclick="import('static/b.js')">b</button>
  • <script type=loadbundle> で loadbundle JSON manifestを記述し、読み込む。
  • あとは通常通りHTMLから利用するだけ

loadbundle JSON manifestのpathsに記述した a.js や b.js が必要になったタイミングでResource bundles であるpack.rbnを取りに行く。

それでは、実際に取りに行く流れを確認する。

rbnファイルの取得

ブラウザが a.js が必要になったときに、pack.rbnを取得する流れを確認する

f:id:ASnoKaze:20210209235441p:plain

  • loadbundle JSON manifest部分のpathに、a.jsはチャンクIDとして bGpobG, FzZGZq を持つことが分かる
  • ブラウザはpack.rbnをリクエストする際にヘッダとして「Resource-Bundle-Chunk-Ids: bGpobG FzZGZq」を指定する
  • a.jsが依存している(importしている) common.jsも含めて rbn ファイルがレスポンスとして返される

(rbnファイルは動的に生成される事になるが、その点についてもFAQで補足されている)

取得したrbn ファイルから、各リソースを取り出して利用する。

rbnファイルの中身

それでは、次にResource bundlesファイルの中身を見ていく。
最初に述べたようにResource bundlesには複数のHTTPレスポンスが格納されています。各リソースを取り出すのにrbnファイル全体を読む必要はなく、indexから必要なファイルを探してそこだけ取り出せるようになっている。

なおrbnはCBOR形式で記述されていてる。

具体的なフォーマットは下記のとおりである

resourcebundle = [
  magic: h'F0 9F 8C 90 F0 9F 93 A6',
  version: bytes .size 4,
  section-lengths: bytes .cbor section-lengths,
  sections: [* any ],
  length: bytes .size 8,  ; Big-endian number of bytes in the bundle.
]
section-lengths = [* (section-name: tstr, length: uint) ],

magic, version, section-lengths, 任意数のsections, lengthから構成される

sectionsには、2種類のsectionが任意数入る

  • indexセクション: 各"リソースPATH"から"そのリソースの格納位置(offset)"へのハッシュ
  • resourcesセクション: HTTPレスポンスが入ってる

各レスポンスは、下記の通りヘッダとペイロードのデータが格納されている

response = [headers: bstr .cbor headers, payload: bstr]
headers = {* bstr => bstr}

その他(標準化やツールについて)

W3CのWICGや、IETF WebPackaging WGへの持ち込みが検討されている模様。(追記 2/24に実際にWICGに移管された)

Webページを再配布可能にするWeb Packaging/Web Bundlesと一部競合しそうだが、WebPackagingを進めているGoogleのJeffrey Yasskin氏やYoav Weiss氏とも連携を取っている模様。ココらへんのトピックもFAQで補足されている。

また、実際にこの仕組を扱うには各種ツールが必要なので、それについても紹介されている
https://github.com/littledan/resource-bundles/blob/main/subresource-loading-tools.md

おわりに

実際、サブリソースのローディングに特化しているので、その点は良さそうな気がしている。

が、僕はFrontendに詳しくないのでそっちの方々がどう思うかは聞いてみたい