すぐにできる! au 無料ゲームにおけるフロントエンドパフォーマンス改善事例

はじめに

こんにちは、フロントエンドエンジニアの中畑 (@yn2011) です。

au Webポータル 無料ゲーム では様々なフロントエンドのパフォーマンス最適化に取り組んでいます。今回は、既に実施した最適化の中から対応コストが小さく、効果が分かりやすかったものを中心に、対応事例のご紹介をします。

なお、au Webポータル 無料ゲーム は Jamstack 構成となっており、Next.js (SSG) と ヘッドレスCMS を利用しております。詳細な技術スタックについてはヘッドレス CMS 運用におけるデプロイと環境差分についてに記載がありますので、ぜひこちらもご覧ください。

Lighthouse による測定結果

パフォーマンスの最適化を行う前の状態で、Lighthouse の測定結果は以下のようになっていました。

image

指標指標の意味結果
First Contentful Paintテキストまたは画像が初めてペイントされるまでにかかった時間2.3 秒
Speed Indexページのコンテンツが取り込まれて表示される速さ3.7 秒
Largest Contentful Paint最も大きなテキストまたは画像が描画されるまでにかかった時間9.1 秒
Time to Interactiveページが完全に操作可能になるのに要する時間8.9 秒
Total Blocking Timeタスクの処理時間が 50 ミリ秒を上回った場合の、コンテンツの初回描画から操作可能になるまでの合計時間1,720 ミリ秒
Cumulative Layout Shiftビューポート内の視覚要素がどのくらい移動しているか0

測定結果を見ると、Largest Contentful Paint、Total Blocking Time は特に改善の余地がありそうでした。今回は、この測定結果を元に、実装コストが小さく・既存の機能に対する影響も小さいと判断した以下の 3 つの最適化についてご紹介します。

ピックアップバナーの preload

au Webポータル 無料ゲーム のトップページは以下のようになっており、画面の大部分をゲームタイトルのバナー(ピックアップバナー)が占めています。

image

トップページにアクセスした際に、ピックアップバナーの画像を優先的に読み込むことで Largest Contentful Paint の数値を改善しユーザが体験する画面表示までの速度を高めることができます。

実装としては、以下のような要素を HTML に追加するだけで実現可能です。

<link rel="preload" href="https://url-to-image" as="image" />

外部ドメインに対するリクエストの preconnect

au Webポータル 無料ゲーム では、広告の表示やデータ分析のために外部ドメインに対してリクエストを行います。

リクエスト先の外部ドメインが予め分かっている場合には、事前に DNS Lookup、Initial Connection、SSL を済ませておくことでリクエストが要求されてからレスポンスを得るまでの時間を短縮することができます。

実装としては、以下のような要素を追加するだけで実現可能です。

<link rel="preconnect" href="//other.domain.com" />

Chrome DevTools の Network タブを利用して確認すると、preconnect 指定前は以下であったのに対し

image

preconnect 指定後に確認すると、各フェーズに使用される時間が短くなりました。

image

なお、preconnect の他に、dns-prefetch を属性値として指定する方法もあります。両者の特徴を比較すると、preconnect の方が多くの処理を事前に行うことができる反面、ブラウザのサポート範囲は dns-prefetch よりは狭いです。

dns-prefetchpreconncet
ブラウザサポート
DNS Lookup
Initial Connection、SSL

以下のようにフォールバックとして両方を指定することも可能なそうですが、WebKit では動作しないという報告 がありました。

<link rel="dns-prefetch preconnect" href="//other.domain.com" />

preconnect は、あくまでパフォーマンス改善のための nice to have な対応であり一部ブラウザが対応していなくても動作自体に影響はないため preconncet のみを利用する判断をしました。

Google オプティマイズのスクリプトを常に読み込まない

au Webポータル 無料ゲーム では、A/B テストを実施するために、Google オプティマイズを利用しています。そのため、Google オプティマイズのスクリプトを必ず読み込んでいました。

<script src="https://www.googleoptimize.com/optimize.js?id=OPT_CONTAINER_ID" />

しかし、A/B テストは常に実施しているわけではないため A/B テストを実施していない期間については不要なリソースの読み込みとスクリプトの実行が発生してしまっていました。

au Webポータル 無料ゲーム は、Next.js の SSG を利用しているので、 A/B テストを実施していなければビルド時に Google オプティマイズのスクリプトタグを含めないように変更しました。

A/B テストを実施しているかの判定は、利用しているヘッドレスCMS に特定のレコードが存在するかどうかで行っています。

実装としては以下のように、_app.tsxgetInitialProps で 判定を行い Props にフラグを追加しました。

static async getInitialProps() {
  ...
  return { pageProps: {}, isOptimizeRunning: !!config.experiments && config.experiments.length > 0 }
}

この isOptimizeRunning を利用し、レンダー時にスクリプトタグを含めるかどうかを決定します。

  {isOptimizeRunning && (
    <script src="https://www.googleoptimize.com/optimize.js?id=%24%7BOPT_CONTAINER_ID" />
  )}

改善の結果

以上の最適化を行い、Lighthouse を再実行すると測定結果は以下になりました。

image

指標改善率(%)
First Contentful Paint2.3 秒1.3 秒43.48
Speed Index3.7 秒3.0 秒18.92
Largest Contentful Paint9.1 秒6.2 秒31.87
Time to Interactive8.9 秒8.6 秒3.37
Total Blocking Time1,720 ミリ秒1,550 ミリ秒9.88
Cumulative Layout Shift000.00

画面描画に関する指標(First Contentful Paint、Speed Index、Largest Contentful Paint)を中心に大きく数値が改善しました。トータルのスコア自体も 38 → 44 と上昇しました。

おわりに

au Webポータル 無料ゲーム で実施したフロントエンドパフォーマンスの最適化についてご紹介しました。

個人的には、パフォーマンスの最適化というと難しそう、調査や実装に多くの時間が必要になりそう、といった先入観がありましたが、意外と簡単に実装できて成果に繋げられるものもあるんだな、という学びがありました。

今回ご紹介したものは、実際に取り組んだパフォーマンスの最適化の中の一部であり、実際にはもっと実装の変更が大きく、既存機能への影響を考慮しなけばならないものも多くありました。それらについては、また別の機会にお伝えできればと思います。

最後までお読み頂きありがとうございました。