Islands in the byte stream

Technical notes by a software engineer

webpack v4.35.3 の JSON loader は JSON.parse() を実行時に行うコードを生成する

ちょっとまえに The cost of JavaScript in 2019 · V8 というブログが話題になりました。

このなかで次のように説明している箇所があります:

As long as the JSON string is only evaluated once, the JSON.parse approach is much faster compared to the JavaScript object literal, especially for cold loads.

(JSON stringを一度しか評価しないのであれば、オブジェクトリテラルよりも JSON.parse のほうがはるかに高速です。コールドロード(サイトの初回アクセス時、何のキャッシュも利用できないケース)では特にそうです。)

この巨大なJSONリテラルは、たとえばwebpackの組み込み JSON loader で *.json を読み込むときに作られていました。たとえば、I18Nをしているサイトにおける翻訳メッセージファイルなどは典型例です。

件のエントリは主にV8について触れていますが、「JSONのほうが言語仕様的にシンプルなので速い」というのはおそらくどのJSエンジンでも真でしょう。だったらwebpackの JSON loader が JSON.parse() ベースのコードを生成すればいいのでは!ということでPRを出してみました。

PR: [performance] use JSON.parse(jsonSource) for JSON modules by gfx · Pull Request #9349 · webpack/webpack

このパッチは webpack v4.35.3 に含まれているので、最新のwebpack を使っているかぎり、何も気にしなくてもこの変更が適用されて高速になるはずです。

なお、2019年7月9日現在β版である Chrome 76 / v8 7.6 では JSON.parse() が2倍以上高速になっているとのこと:

V8 release v7.6 · V8

このwebpackの変更はChrome 76以降はさらに効果的でしょう。

実際のパフォーマンスはJSの性質によって違うと思われるので、もし余裕があればプロダクションでlighthouseなどを使って測定してみてほしいです。

なお、この機能自体はwebpack built-in json loaderを使う限り自動的に有効になりますが、json-loader moduleを使うことで無効化できます。

const config = {
  // ...
  module: {
    rules: [
      {
        test: /\.json$/,
        type: "javascript/auto",
        use: {
          loader: "json-loader",
        },
      },
    ],
   // ...
 }
};