Next.jsのプロジェクトにおける、フロントエンドのパフォーマンスについて考える(Pages Router)
はじめに
こんにちは。Belong でフロントエンドエンジニアをしている ryo です。
プロジェクトでフロントエンドのパフォーマンスについて考える機会があったため、どのように向上していけるのか自分なりに整理しました。
なお、実践については現在進行中で、調べた上ではありますが、推測の部分もあり、間違いがあるかもしれません。その前提でお読みいただけると幸いです。
また、今回は Next.js の Pages Router について考えており、App Router とは異なる部分もあるかもしれないため、その点もご了承ください。
メトリクスについて
なんとなくパフォーマンスに不満があっても、曖昧なままでは改善が難しく、また、正しい方向に進めないため、指標が必要だと思います。
Core Web Vitalsを一番大切にしつつ、Web Vitals で挙げられる指標全般を追うことで改善を行っていきたいと思っています。
アプローチについて
__NEXT_DATA__
の量を減らす
__NEXT_DATA__
とは
Next.js はサーバー側の処理結果や、ランタイム時に参照したいデータを <script id="__NEXT_DATA__" type="application/json">...</script>
の形式で渡しています(HTML の要素として出力している)。
__NEXT_DATA__
は基本的にはこれのことを指している理解で大丈夫かと思います。(少なくとも、本ブログではそれを指しています)
ちなみに、Next.js を利用しているページの Chrome Dev tools のコンソール上でJSON.parse(document.getElementById("__NEXT_DATA__").textContent)
を実行することで、__NEXT_DATA__
の中身を確認することができます。
Large Page Data の警告
私の体験談ですが、Next.js のプロジェクトで以下のような警告が出ることがありました。
Warning: data for page "/hoge" is 234 kB which exceeds the threshold of 128 kB, this amount of data can reduce performance.
これは、Next.js が定めている __NEXT_DATA__
の 閾値 (128kb) を超えると警告が出るようになっています。
なぜ 128kb がデフォルトの閾値なのかは分かりませんでしたが、、パフォーマンスを最優先する Next.js が設定した値ですので、何か背景があるのだと思います。
これ以下になるようには目指したいところです。
(恐縮ながら、https://github.com/vercel/next.js/discussions/72792 で質問してみました、皆さんも興味があれば ↑
を、もしくはご回答をいただけると嬉しいです。)
影響のあるメトリクス
- TBT
- Parse HTML & CSS
- HTML サイズが小さくなるため
- Script Evaluation
- Javascript の実行時間
__NEXT_DATA__
のサイズが小さくなり、不要だった javascript 処理が行われなくなるため
- Parse HTML & CSS
クライアント側での計算量を減らす
いわゆる技術的負債によるもので、サーバー側でさせるべき計算を一部クライアント側で行ってしまっている事があります。
クライアントでの計算速度は環境に依存しますし、サーバー側のより優れた環境で計算させた方が処理速度が上がり、メトリクスに良い影響があると思っています。
影響のあるメトリクス
- INP
- クライアント側での計算量が減るため
- FCP , TBT
First Load JS shared by all のサイズを減らす
First Load JS shared by all
とは
app.tsx
を始めとした、web アプリ全体で利用される共通のファイルのことです。next build
した時に表示される、以下のようなものを指しています。
First Load JS shared by all 300 kB
├ chunks/framework-945b357d4a851f4b.js 100 kB
├ chunks/main-c2de32f30977b390.js 100 kB
├ chunks/pages/_app-e233c8701c182eb3.js 100 kB
├ css/e7ec9bc895c4b94b.css 20 B
└ other shared chunks (total) 20 B
影響のあるメトリクス
- TBT
- FCP
画像を最適化する
画像ファイルはページで利用するアセットの中でも、単体としてかなりサイズが大きく、パフォーマンスに影響を与える要素だと思います。以下のようなアプローチが出来そうです。
オフスクリーン画像の遅延読み込みを行う
こちらで提示されている解決方法ですが、
ファーストビューに含まれないようなオフスクリーンの画像は遅延読み込みを行うことで、メインスレッドをブロックする事を防げそうです。
フォーマットを見直す
こちらで提示されている解決方法です、
取り組み方法も比較的簡単で、画像のサイズを削減できそうです。
影響のあるメトリクス
- FCP
- LCP
- TBT
不要な javascript を減らす
実装時に意識できず、クライアントに送る必要のない javascript も送ってしまっている可能性もあるかもしれません。これは Script Evaluation
のステップで不要にメインスレッドをブロックしてしまう可能性があると思います。
影響のあるメトリクス
- TBT
不要な CSS を減らす
javascript と同様に、不要な CSS も送ってしまっている可能性があります。単純に不要なデータ量をユーザーの通信に使わせてしまいますし、これは Parse HTML & CSS
のステップで不要にメインスレッドをブロックしてしまう可能性があると思います。
影響のあるメトリクス
- TBT
計測について
次に、対象のプロジェクトのメトリクスをどのように計測して、確認するかについて整理していきます。
Lighthouse
Lighthouse とは
ラボデータを元に Web Vitals を測定できるものです。
本番環境へのデプロイ前に Web Vitals の測定を取ることができ、開発時にチェックすることができます。
計測できる Web Vitals
ラボデータを元にした Web Vitals の計測ができます。
計測できない Web Vitals
こちらに記載のある通り、
ラボ
は読み込みをシミュレートするものであり、ユーザー操作を考慮しないため、 INP 等一部正確に測定できない指標があります。
また、lighthouse を実行した環境 (ラボ環境)に依存してしまうため、実際のユーザーの環境とは異なる可能性はあり、留意する必要がありそうです。
Page Speed Insights
lighthouse のスコアと合わせて、Chrome ユーザーエクスペリエンス レポートを利用した、
フィールドデータを元にした Web Vitals も確認できるツールです。
INP 等、LightHouse スコアに出ない指標を確認できます。
個人的には、サクッと本番環境のパフォーマンスの全体感を見るのに便利だと感じました。
API もあります。
Google Search Console
自身が管理者のドメインの、フィールドデータを元にした Web Vitals を確認できます。
ユーザーの来訪ベースで確認できるため、取り組むページの優先順位をつけられそうなのも、利用するデータとして良いと思いました。
@next/bundle-analyzer
Next.js プロジェクトの javaScript バンドルのサイズを確認できるものです。
Script Evaluation
カテゴリが異常に大きい場合、この結果を利用してあたりをつけることが出来そうでした。
First Load JS shared by all
のサイズ削減に取り組む際等、開発環境で確認しつつ Web Vitals の指標も合わせて見ることで、効果的な改善ができそうだと思います。
継続的なチェックについて
計測方法まで整理しましたが、ではどのように継続して計測を行い、改善していくのかを整理したいと思います。
何をチェックするのか
個人的には以下の計測結果を継続的にチェックしていきたいと思っています。
Google Search Console
のエクスペリエンス
に関するレポート (フィールドデータ)- 各ページの
LightHouse
スコア(ラボデータ) - javascript のバンドルサイズ(ラボデータの補強)
どうチェックするのか
LightHouse CI でラボデータを蓄積する
- LightHouse CIを利用して、継続的にラボデータを蓄積しようと思っています。
CI/CD で @next/bundle-analyzer の結果を蓄積する
@next/bundle-analyzer
CI/CD で実行し、その結果ファイルを蓄積したいと思っています。
ダッシュボードを用意する
上記に記載した蓄積データと、Google Search Console
の エクスペリエンス
のレポートを利用して、グラフ等で視覚的に状態の分かるダッシュボードを作成しようと思っています。
ダッシュボードの作成には、現時点では Looker Studio を利用することを検討しています。
合わせて SEO の平均順位等、ビジネス的に気になる指標も見てもいいかもしれないなと思っています。
良好な状態を継続していくために
ここまで整理した内容で、計測して、それをチェックし、改善に取り組んでいくというわりと綺麗な PDCA サイクルを作れそうに個人的には思っています。 最後に、どう維持していけるかも整理させてください。
パフォーマンスバジェットを導入する
パフォーマンスバジェットを開発サイクルに導入することで解決をすることが出来そうかな、と思いました。
以下の文章が個人的にとても腹落ちしました:
スピードを最適化したブランドは、すぐに後退してしまうことがよくあります。なぜなら、ウェブサイトのパフォーマンスは健康づくりによく似ているからです。1 回の努力だけでは不十分で、ライフスタイルを変えなければなりません。
正直まだ具体的な指標については考えておりませんが、これを導入する事で、パフォーマンス改善に関するチケットの温度感を、適切に測ることが出来そうです。
終わりに
このブログで書いたようなことを実践して、パフォーマンスを向上させていきたいと思っています。その後どうなったかもブログで発信していけたらとも思っています。
また、Core Web Vitals の改善は SEO の観点でも重要であり、
パフォーマンスが高い、つまりユーザー視点でレスポンスが早いというのは UX の観点でもとても重要だと思っています。
そんな作業ですが、現在も取組中で、それ以外の業務を続けながら、Web アプリ全体で取り組んでいくのは個人的にはなかなか途方もない作業に感じています。。一緒に取り組んでくれる方がいれば嬉しいです。
興味がある方は エンジニアリングチーム紹介ページ をご覧いただけると幸いです。
最後までお読みいただき、ありがとうございました。
参考資料
- https://www.builder.io/blog/why-react-server-components
- https://nextjs.org/docs/messages/large-page-data
- https://medium.com/@bvjebin/winning-the-millisecond-race-performance-optimization-of-a-next-js-site-962822f19379
- https://web.dev/articles/how-to-stay-fast?hl=ja
- https://akifumisato.github.io/slide-of-nextjs-15-event/1
- https://zenn.dev/takepepe/articles/nextjs-getlayout-chunk-relation