React.lazy を使うと初回表示時のパフォーマンスが落ちた事例の紹介

こんにちは! フロントエンドエンジニアのもりやです。

今回はママリのアプリ内で使われている WebView に React.lazy を導入した結果、初回表示時のパフォーマンスが落ちてしまった事例を紹介します。

React.lazy を入れようと思った動機

ママリでは、アプリ内の一部の画面をA/Bテストしやすくするなどの目的で、リリースが容易な WebView を使っています。

ただ WebView の初回表示が遅いという課題があり、今回パフォーマンス改善に取り組んでいました。

その改善策の1つとして React.lazy を導入して効果を検証してみました。

React.lazy とは

詳しくは公式ドキュメントの コード分割 – React に書かれていますが、ざっくりいうとファイルを分割して遅延読み込みができる機能です。

ママリで使っている WebView は react-router でパスごとに表示するページを切り替えていますが、ビルド自体は1つのファイルで出力しています。
(正確には外部ライブラリ用にもう1つ出力しています)

そのため本来表示したいページ以外のデータも含めて全部ダウンロードしないと表示ができない状態でした。

ファイルを分割してダウンロード量を減らせば、初回ロード時のパフォーマンスが上がるのでは、という意図で導入してみました。

初回表示時のパフォーマンス計測方法

Google Chrome の DevTools の Performance タブで計測しました。

また以下の設定もしています。

  1. Fast 3G にする
  1. Disable Cache にチェックを入れる

image.png

React.lazy 導入前

Before performance

bundle.js(と外部ライブラリをまとめた vendor.bundle.js ) の読み込みが特に時間がかかっているので、ここを削減しようと考えました。

今回 React.lazy を導入して bundle.js を複数のファイルに分割するようにしました。

React.lazy 導入後

結論から言うと、ロードにかかる時間がかえって増加してしまい、初回表示時のパフォーマンスが下がる結果になりました。

After performance

bundle.js 自体のサイズは 157KB51.4KB に削減され、新たに読み込まれるようになったチャンクデータも合計 14KB なので読み込む容量自体は減らすことに成功しています。

しかし bundle.jsvender.bundle.js の読み込みが終わってからチャンクデータの読み込みが開始されるので、その分遅くなってしまうという結果になってしまいました。

いったんパフォーマンスが落ちていることが確認できた時点で、Revertとリリースして React.lazy の対応を消しました。

考察

React.lazy を使った場合、必要な全てのスクリプトが読み込まれ評価された後に、チャンクデータが読み込まれるようです。

なので vendor.bundle.js の読み込み時間が変わらないと、チャンクしたデータの読み込み分遅くなってしまうという結果になりました。

そもそも vendor.bundle.js の読み込みにも時間がかかってパフォーマンスが低下しているので、ここの改善の方が重要になりそうです。

また vendor.bundle.js が十分に小さければ、全体のロード時間が短縮され React.lazy による改善効果があったかもしれません。
なるべく外部ライブラリを導入しない、というのもパフォーマンスに有効かもしれませんね。

まとめ

React.lazy を入れることでロード時間が削減できる時間が、どれだけパフォーマンスに良い影響を及ぼすのかを考えて導入する必要がある、という学びを得ました。

今回の外部ライブラリのサイズによる影響のように、環境によって React.lazy を入れることによる影響は変わってきます。
一概に導入する・しない方が良いとは言えないのが難しいところですが、面白ところでもあると思いました。

最後に、コネヒトではフロントエンドを一緒に改善していただけるエンジニアを募集しています!

[https://hrmos.co/pages/connehito/jobs/00e:embed:cite]

付録: ChankLoadError

パフォーマンスと直接関係はないのですが、React.lazy を使うように変更してリリースすると ChunkLoadError というエラーが発生するようになりました。
文字通り React.lazy を使って分割したファイルが読み込めないという感じのようです。

件数もユーザー数に対する割合としては非常に少なく、再現は難しい状況でした。
一応、ブラウザの DevTools でネットワークを切断したりすると再現はできるのですが、なぜユーザーの環境で起きているのかは不明でした。

検索しても自動でリロードする対応をすると良いよ、ぐらいの記事しか見つかりませんでした。
結局 React.lazy を使わないように戻したので、解決はしていません。

想像ではありますが、クライアントのネットワーク環境とかも影響あるかもなので、完全に解決するのは難しいかもしれません。

この記事はShodo (https://shodo.ink) で執筆されました。


※この記事は以下に投稿した記事のクロスポストです
https://tech.connehito.com/entry/2021/03/23/082144