PWA(Progressive Web Apps)対応サイトの作り方・実装方法まとめ - AWS上で学習したPWA導入例とLighthouse Report Viewerの使い方

小西秀和です。
現在、AWSの静的ウェブサイトホスティングで入門するAWS Amplify(Console、CLI) - 概要編などAWSのサーバーレスな静的ウェブサイトホスティングをテーマにしたブログ記事を執筆しています。
今回もAWSは関係しますが、フロントエンドに近い話題で、最近動向が本格的になってきたと感じているPWA(Progressive Web Apps)について私が自主的に学習した内容を記事にしてみました。

私の場合はAmazon S3、Amazon CloudFront、AWS Certificate Manager、Amazon Route53を使用した静的ウェブサイトに最小限のコンテンツをデプロイしてPWAの実装について学習しました。
また、Lighthouse Report ViewerというPWAなどの品質テストができるツールを使用して、PWAの基準に合格し、Performance、Accessibility、Best Practices、SEOといったカテゴリでも何回かに1回100点が取れる状態まで対策をしてみました。

以降では、PWAの概要と利点、AWSを使う理由、PWAに対応するために実装した最低限のこと、Lighthouse Report Viewerの使い方、Lighthouseで高得点を出すために配慮したことについて主に記載しています。

※本記事および当執筆者のその他の記事で掲載されているソースコードは自主研究活動の一貫として作成したものであり、動作を保証するものではありません。使用する場合は自己責任でお願い致します。また、予告なく修正することもありますのでご了承ください。

PWA(Progressive Web Apps)とは

PWA(Progressive Web Apps)とは一言でいうとブラウザで表示されるウェブサイトをネイティブアプリ(iOSアプリ、Androidアプリなど)のように使用できるようにする仕組みです。
ウェブサイトをPWAに対応させると、PWAをサポートしているブラウザが使用できるOS(iOS、Android、Windows、macOSなど)にウェブサイトをアプリのような形式で追加できます。
PWA対応したウェブサイトはPWAとしてのみ動作するわけではなく、今まで通りブラウザで参照する通常のウェブサイトとしても動作します。

例えばPWAをサポートしているAndroidのChromeの場合、PWA対応ウェブサイトを表示してオプションメニューの「アプリをインストール」からウェブサイトをアプリとして追加することができ、プッシュ通知を受け取ることもできます。
また、最新のiOSをインストールしているiPhoneでも、SafariでPWA対応ウェブサイトを表示してオプションメニューの「ホーム画面に追加」からウェブサイトをアプリとして追加してスタンドアロンで立ち上げることは可能です。
さらに、最近ではWindows10・11やmacOSなどの最新OSをインストールしたPCでもPWA対応したブラウザを使用すればPWA対応ウェブサイトをアプリのようなインタフェースで使用できるようになってきています。

Google ChromeでPWAをインストールする方法については次の参考資料を参照してください。
PWAインストール参考資料:プログレッシブ ウェブアプリを利用する - パソコン - Google Chrome ヘルプ

ただ、本記事執筆時点では各OSやブラウザでPWAのサポート内容はかなり差があるため、実現しようとしている要件を満たすかどうかはその都度、状況を調査する必要があります

PWAに対応させる利点

私はPWAはウェブサイトとモバイルネイティブアプリの間に位置づけられるのではないかと考えています。
そこで、私が感じたウェブサイトをPWAに対応させる利点、モバイルネイティブアプリから見たPWAの利点を参考として挙げてみます。

ウェブサイトをPWAに対応させる利点には次のようなものが挙げられます。

  • アプリのようにアイコンがOSの画面に追加できる
  • ブラウザではなくスタンドアロンの一つのアプリとしてウェブサイトが使用できる
  • ローカルキャッシュするコンテンツを設定すればオフラインでも起動できる
  • OSやブラウザでサポートされていればプッシュ通知が使用できる

一方でモバイルネイティブアプリから見たPWAの利点には次のようなものが挙げられます。

  • 開発言語を共通化でき、OSへの依存性を少なくできる
  • フロントエンドからバックエンドまでウェブサイトから提供できる
    ⇒アプリストアに登録して主要な機能をまとめてダウンロードし、インストールやアップデートする必要がない
    ⇒キャッシュを破棄すれば常に最新バージョンの機能をウェブサイトから提供できる

PWAの実装環境にAWSを使う理由

PWAは基本的に静的コンテンツをホスティングでき、PWAが動作できるのであればウェブサーバーの種類は特に問われません。
それでも私がPWAの実装環境にAWSのAmazon S3、Amazon CloudFront、AWS Certificate Manager、Amazon Route53の組み合わせを使う理由をいくつか紹介します。

  • Amazon S3の高可用性ストレージ
    1年間に99.999999999%の堅牢性と99.99%の可用性を提供するよう設計されたストレージで配信するコンテンツを保存できる。
  • Amazon CloudFrontのグローバルなエッジロケーションとキャッシュ機能
    グローバルに配置されたエッジロケーションからキャッシュを利用したコンテンツを配信するため、様々な地域からのウェブサイト表示の速度向上が期待できる。
  • AWS Certificate Managerのマネージド型無料パブリック証明書
    更新管理不要で無料のSSL/TLS証明書をカスタムドメインに対して発行し、Amazon CloudFrontなどのAWSサービスに関連付けることができる。
  • Amazon Route53の多機能性
    ドメインのZone Apexに対してAmazon CloudFrontをレコード登録することができ、ヘルスチェックやルーティングなどの機能が充実している。
  • AWS Amplify ConsoleでS3+CloudFront+Certificate Manager+Route53の組み合わせを迅速に構築可能
    上記のAWSサービスを個別に設定する作業を省略したい場合はAWS Amplify Consoleを使用することでブラウザからこれらの組み合わせを容易に作成できる。
    ※AWS Amplify Consoleの使用方法については次の記事を参照してください。
    AWSの静的ウェブサイトホスティングで入門するAWS Amplify(Console、CLI) - 構築編(Amplify Console)
  • コスト最適化
    AWSサービスはコンテンツの保存量やリクエスト量など使用した量に対する従量課金制で、上記のAWSサービスの組み合わせの場合は仮想サーバーの起動コストが発生しないため、比較的安価に運用できる。
    特にコンテンツ量やリクエスト量が比較的少ない今回の例のようなウェブサイトであれば、数百円/月でこれらの高機能が使用可能。
  • ウェブサイトに機能追加する選択肢が豊富
    最小限のコンテンツで用意したウェブサイトを拡張して機能追加をする場合でもAWSには上記のサービスと親和性の高い様々なバックエンドを構築するためのサービスがある。
  • ウェブサイトに機能追加をする場合の技術資料が豊富
    AWSから提供されるドキュメントやサポートに加えて、AWSエンジニアやそのコミュニティのアウトプットなど、AWSはサービス活用のヒントを得られる情報源が豊富。

このうち、特にAmazon CloudFrontは各地のエッジロケーションからコンテンツを配信するため、後述するLighthouse Report ViewerのPerformanceカテゴリに該当するウェブサイト表示時間の短縮に効果的だと考えられます
Amazon CloudFrontはバックエンドにAWS以外のサーバーを指定することもできるため、ページ表示速度の改善策としてAmazon CloudFrontを試してみるということも一つの選択肢でしょう。

PWAに対応するために実装した最低限のこと

ここからはウェブサイトをPWAに対応するために実装したことを例として紹介します。
今回は自主学習の範囲でPWAの実装方法とLighthouse Report Viewerでのチェックに合格することが目的なので、対象としたウェブサイトのメインコンテンツはindex.htmlと最小限の画像のみで構成しました。
それ以外のコンテンツは主にPWA対応に必要なJavaScriptとAndroid、iOSなどの各OSへのアプリ追加で必要となるアイコン画像が中心です。

ウェブサイトにデプロイしたPWA関連ファイル一覧

今回、PWA対応を試すウェブサイトのルートディレクトリから見たPWA関連のファイルの一覧を次に掲載します。
ほとんど各OS用のサイズや用途別のアイコン画像ですが、manifest.jsonとsw.jsというのがPWAを実現する中心的な役割を果たします。
今回は各端末や各OSがアイコンで採用するサイズを深く調査したわけではないので、これだけあれば該当する画像サイズがあるだろうと種類を多めに用意しています。
また、学習目的で旧ブラウザや旧OSに対応するための記述も残しているので無駄なものもあると思います。
そのため、その点の調査ができてターゲットとなるOSやブラウザが決まっていれば、実際はここまで細かくサイズ別の画像を用意しておく必要はないでしょう。
各ファイルの説明については後述します。

[iam@h-o2k hidekazu-konishi.com]$ tree
.
├── ~不要部分は省略し、見やすいように順番変更しています~
├── android-chrome-36x36.png
├── android-chrome-48x48.png
├── android-chrome-72x72.png
├── android-chrome-96x96.png
├── android-chrome-128x128.png
├── android-chrome-144x144.png
├── android-chrome-152x152.png
├── android-chrome-192x192.png
├── android-chrome-256x256.png
├── android-chrome-384x384.png
├── android-chrome-512x512.png
├── android-chrome-maskable-48x48.png
├── android-chrome-maskable-72x72.png
├── android-chrome-maskable-96x96.png
├── android-chrome-maskable-128x128.png
├── android-chrome-maskable-192x192.png
├── android-chrome-maskable-384x384.png
├── android-chrome-maskable-512x512.png
├── apple-touch-icon-57x57-precomposed.png
├── apple-touch-icon-57x57.png
├── apple-touch-icon-60x60-precomposed.png
├── apple-touch-icon-60x60.png
├── apple-touch-icon-72x72-precomposed.png
├── apple-touch-icon-72x72.png
├── apple-touch-icon-76x76-precomposed.png
├── apple-touch-icon-76x76.png
├── apple-touch-icon-114x114-precomposed.png
├── apple-touch-icon-114x114.png
├── apple-touch-icon-120x120-precomposed.png
├── apple-touch-icon-120x120.png
├── apple-touch-icon-144x144-precomposed.png
├── apple-touch-icon-144x144.png
├── apple-touch-icon-152x152-precomposed.png
├── apple-touch-icon-152x152.png
├── apple-touch-icon-180x180-precomposed.png
├── apple-touch-icon-180x180.png
├── icon-16x16.png
├── icon-24x24.png
├── icon-32x32.png
├── icon-36x36.png
├── icon-48x48.png
├── icon-72x72.png
├── icon-96x96.png
├── icon-128x128.png
├── icon-144x144.png
├── icon-152x152.png
├── icon-160x160.png
├── icon-192x192.png
├── icon-196x196.png
├── icon-256x256.png
├── icon-384x384.png
├── icon-512x512.png
├── favicon.ico
├── index.html
├── index.js
├── manifest.json
└── sw.js

各ファイル説明:android-chrome-○x○.png

関連ファイル一覧のうちandroid-chrome-○x○.pngのファイル名形式のものは主にAndroidとChrome向けのアイコンとして作成しました。
AndroidとChromeはPWAを積極的に推進しているGoogleのプロダクトであるため、PWAの設定でも細かいアイコン設定が必要です。
PWAの設定ファイルに相当するmanifest.jsonではandroid-chrome-○x○.pngや次で説明するandroid-chrome-maskable-○x○.pngといったAndroidとChrome向けのサイズ別アイコン画像を指定したほうがよいでしょう(後述のLighthouse Report ViewerのPWA基準のクリアにも必要です)。

各ファイル説明:android-chrome-maskable-○x○.png

関連ファイル一覧のうちandroid-chrome-maskable-○x○.pngのファイル名形式も主にAndroidとChrome向けのアイコンですが、android-chrome-○x○.pngとは用途が異なります。
android-chrome-maskable-○x○.pngは、PWAのmaskable iconに対応した画像です。
maskable iconとはセーフゾーンという円形領域にデザインが収まるアイコン画像のことです
セーフゾーンとは正方形の画像の一辺を100%としたときに、正方形の中心から40%を半径とする円の領域を指します
セーフゾーン参考資料:W3C - Web Application Manifest - Icon masks and safe zone

つまり、このセーフゾーン内にデザインが収まるアイコン画像を各サイズ別に作成したものが、今回の例として挙げているandroid-chrome-maskable-○x○.pngです。

maskable iconの作成はノウハウなしに自作すると結構大変ですので、次の「Maskable​.app」というサイトで作成すると簡単です。

■maskable icon作成手順

  1. https://maskable.app/editorにブラウザで遷移する f:id:uio8:20210903193008p:plain
  2. 右上の「Layers」の「Upload」から対象画像をアップロードする
  3. アップロード画像が表示された画面中央下の「Masks」で「Minimum safe area」を選択する
  4. 右側のサイドメニューでPaddingや背景色などを調整して領域にデザインが収まるように調整する
  5. アップロード画像が表示された画面中央上の「Export」から作成した画像をサイズを指定してダウンロードする。

各ファイル説明:apple-touch-icon-○x○.png

関連ファイル一覧のうちapple-touch-icon-○x○.pngのファイル名形式のもの主にiPhone、iPadなどのApple製品向けのアイコン画像です。
ウェブサイトが「ホーム画面に追加」された場合のアイコン画像として使用されます。

各ファイル説明:apple-touch-icon-○x○-precomposed.png

関連ファイル一覧のうちapple-touch-icon-○x○-precomposed.pngのファイル名形式のものは古いiOSでアイコンに光沢処理をしない画像を指定するものです。
現在では基本的に不要ですが、今回は学習目的なのでこのような仕様もあったことを記録として残すために記載しています。

各ファイル説明:icon-○x○.png

上記のアイコン以外を使用するOSやブラウザなどに汎用的に対応するためのアイコン画像です。

各ファイル説明:favicon.ico

favicon.icoは従来から使用されているブラウザ向けのアイコン画像です。
favicon.icoは前述のicon-48x48.png~icon-192x192.pngまでの8種類のサイズをまとめてマルチアイコンにしています。

各ファイル説明:manifest.json

manifest.jsonはPWAを導入する場合に必須となるアプリ設定の役割をするファイルです。
ここでは今回の例で使用したmanifest.jsonをベースに説明していきます。
その他、manifest.jsonの詳細については次の参考資料を参照してください。
manifest.json参考資料:W3C - Web Application Manifest - Web Application Manifest

まず、今回の例で使用した各キーの説明を次に示します。

name:アプリ名
short_name:アプリ省略名
description:アプリの説明
start_url:アプリのスタート画面となるパスの指定
display:アプリの表示形式(standalone:単独アプリ形式、browser:ブラウザ表示のまま、など)
orientation:画面の向きを指定(any:どの向きでも対応、など)
background_color:アプリの背景色を指定(#FFFFFF、など)
theme_color:アプリのテーマカラーを指定(#FFFFFF、など)
icons:アプリのアイコンとして使用する画像のリスト
  src:アイコン画像のパスを指定(/android-chrome-152x152.png、など)
  sizes:アイコン画像のサイズを指定(152x152、など)
  type:アイコン画像の形式を指定(image/png、など)
  purpose:アイコン画像がセーフゾーンに収まる円形表示対応していること(maskable)などを指定

次に今回の例で使用した各キーの設定例を示します。
上記の各キーをもれなく用意することと前述したandroid-chrome-○x○.pngおよびandroid-chrome-maskable-○x○.pngをサイズ別にicons配列に設定するところがポイントとなります。

{
  "name":"<アプリ名>",
  "short_name":"<アプリ省略名>",
  "description":"<アプリの説明>",
  "start_url":"/",
  "id":"/",
  "display":"standalone",
  "orientation":"any",
  "background_color":"#ffffff",
  "theme_color":"#ffffff",
  "icons": [
    {
      "src": "/android-chrome-36x36.png",
      "sizes": "36x36",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-48x48.png",
      "sizes": "48x48",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-72x72.png",
      "sizes": "72x72",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-96x96.png",
      "sizes": "96x96",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-128x128.png",
      "sizes": "128x128",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-144x144.png",
      "sizes": "144x144",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-152x152.png",
      "sizes": "152x152",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-256x256.png",
      "sizes": "256x256",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-384x384.png",
      "sizes": "384x384",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-maskable-48x48.png",
      "sizes": "48x48",
      "type": "image/png",
      "purpose": "maskable"
    },
    {
      "src": "/android-chrome-maskable-72x72.png",
      "sizes": "72x72",
      "type": "image/png",
      "purpose": "maskable"
    },
    {
      "src": "/android-chrome-maskable-96x96.png",
      "sizes": "96x96",
      "type": "image/png",
      "purpose": "maskable"
    },
    {
      "src": "/android-chrome-maskable-128x128.png",
      "sizes": "128x128",
      "type": "image/png",
      "purpose": "maskable"
    },
    {
      "src": "/android-chrome-maskable-192x192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "maskable"
    },
    {
      "src": "/android-chrome-maskable-384x384.png",
      "sizes": "384x384",
      "type": "image/png",
      "purpose": "maskable"
    },
    {
      "src": "/android-chrome-maskable-512x512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "maskable"
    }
  ]
}

各ファイル説明:sw.js

sw.jsはPWAにおけるService Workerを実行するJavaScriptです。
Service WorkerとはブラウザがウェブサイトをPWAとして動作させる際にバックグラウンド処理やプッシュ通知などを実行するスクリプトです

Service Workerで利用可能なイベントには、install、activate、fecth、massage、sync、pushといったものがあり、それぞれのイベントのタイミングで処理する内容をスクリプトで記述していくことになります。
このService Workerのイベントとライフサイクルについては次の参考資料が役に立ちます。
Service Worker参考資料:
Service Worker の紹介  |  Web Fundamentals  |  Google Developers
Service worker の使用 - Web API | MDN

特に最近では後述するLighthouse Report ViewerでPWAの基準を満たすためにはinstall、fetchイベントを用意してオフラインで動作できることが必要になりました
(Google Chrome 89あたりからデベロッパーツールで警告が出るようになりました。)
そのため、今回の例でも要件に必要なイベントを用意し、ウェブサイト表示に必要なコンテンツをキャッシュする最低限のロジックをService Workerに追加しています。

'use strict';

const CACHE_NAME = '<キャッシュ名>';
const urlsToCache = [
  "<キャッシュするファイルパス1>",
  "<キャッシュするファイルパス2>",
  //・・・
  "<キャッシュするファイルパスX>"
];

//installイベントの場合
//前述のファイルパスをすべてキャッシュに登録する
self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(CACHE_NAME).then(function(cache) {
      return cache.addAll(urlsToCache);
    })
   );
});

//fetchイベントの場合
//ウェブサイトへのアクセスが成功すれば取得できた内容をキャッシュに保存した上でアプリで表示する。
//ウェブサイトへのアクセスが失敗すれば保存されているキャッシュをアプリで表示する。
self.addEventListener('fetch', function(event) {
   event.respondWith(async function() {
      try{
        var res = await fetch(event.request);
        var cache = await caches.open(CACHE_NAME);
        cache.put(event.request.url, res.clone());
        return res;
      }
      catch(error){
        console.log('Using cache');
        return caches.match(event.request);
       }
     }());
 });

各ファイル説明:index.js

index.jsはindex.htmlから読み込み、Service Workerであるsw.jsを登録するためのJavaScriptです。
今回は単純にService Workerの登録処理しか入れていません。

if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/sw.js').then(function(registration) {
      //Service Worker(sw.js)が登録できたら特に何もしない。
    }, function(err) {
      //Service Worker(sw.js)の登録が失敗した場合はブラウザにログを出す。
      console.log('ServiceWorker registration failed: ', err);
    });
  });
}

各ファイル説明:index.html

index.htmlはウェブサイトのメインコンテンツで、HTMLのheadタグ内にウェブサイトとしてのアイコン、PWAに必要なmanifest.json、index.jsを設定しています。
今回の例で関係する部分を抜き出して記述すると次のようになります。

<head>
<!--~不要部分は省略し、見やすいように順番変更しています~-->
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" sizes="36x36" href="/android-chrome-36x36.png">
<link rel="icon" type="image/png" sizes="48x48" href="/android-chrome-48x48.png">
<link rel="icon" type="image/png" sizes="72x72" href="/android-chrome-72x72.png">
<link rel="icon" type="image/png" sizes="96x96" href="/android-chrome-96x96.png">
<link rel="icon" type="image/png" sizes="128x128" href="/android-chrome-128x128.png">
<link rel="icon" type="image/png" sizes="144x144" href="/android-chrome-144x144.png">
<link rel="icon" type="image/png" sizes="152x152" href="/android-chrome-152x152.png">
<link rel="icon" type="image/png" sizes="192x192" href="/android-chrome-192x192.png">
<link rel="icon" type="image/png" sizes="256x256" href="/android-chrome-256x256.png">
<link rel="icon" type="image/png" sizes="384x384" href="/android-chrome-384x384.png">
<link rel="icon" type="image/png" sizes="512x512" href="/android-chrome-512x512.png">
<link rel="icon" type="image/png" sizes="16x16" href="/icon-16x16.png">
<link rel="icon" type="image/png" sizes="24x24" href="/icon-24x24.png">
<link rel="icon" type="image/png" sizes="32x32" href="/icon-32x32.png">
<link rel="icon" type="image/png" sizes="36x36" href="/icon-36x36.png">
<link rel="icon" type="image/png" sizes="48x48" href="/icon-48x48.png">
<link rel="icon" type="image/png" sizes="72x72" href="/icon-72x72.png">
<link rel="icon" type="image/png" sizes="96x96" href="/icon-96x96.png">
<link rel="icon" type="image/png" sizes="128x128" href="/icon-128x128.png">
<link rel="icon" type="image/png" sizes="144x144" href="/icon-144x144.png">
<link rel="icon" type="image/png" sizes="152x152" href="/icon-152x152.png">
<link rel="icon" type="image/png" sizes="160x160" href="/icon-160x160.png">
<link rel="icon" type="image/png" sizes="192x192" href="/icon-192x192.png">
<link rel="icon" type="image/png" sizes="196x196" href="/icon-196x196.png">
<link rel="icon" type="image/png" sizes="256x256" href="/icon-256x256.png">
<link rel="icon" type="image/png" sizes="384x384" href="/icon-384x384.png">
<link rel="icon" type="image/png" sizes="512x512" href="/icon-512x512.png">
<link rel="manifest" href="/manifest.json">
<script src="/index.js"></script>
</head>

Lighthouse Report Viewerの使い方

ここからはGoogleが提供するLighthouse Report Viewerの使い方を紹介します。
Lighthouse Report ViewerはPerformance、Accessibility、Best Practices、SEOといったカテゴリについて100点満点でウェブサイトの対策状況を評価し、PWAについても基準を満たしているかを確認できるサイトパフォーマンス測定ツールです。
ただし、Lighthouse Report Viewerの評価とウェブサイトが有用なコンテンツを発信しているかは関係ありません
そのため、本当の意味でウェブサイトの価値を高めるためにはサイトパフォーマンスと有用なコンテンツ発信の両方を意識する必要があると考えられます。
その点を考慮にいれてサイトパフォーマンスを評価するLighthouse Report Viewerの使い方を見ていきましょう。

Google ChromeなどのデベロッパーツールからLighthouseを起動する方法

PC版のGoogle Chrome、Microsoft Edge、VivaldiなどChromiumベースのブラウザであればGoogle Chromeと同様のデベロッパーツールが使用できます。
このデベロッパーツールの中にあるLighthouse Report Viewerの実行機能を使用するのがこの方法になります。

特にGoogle ChromeのBeta版など先行リリースされているものでは今後導入が予定されているLighthouseの新しいバージョンが使用できることがあるため、新しい基準での評価が必要な場合は試してみるとよいと思います(実際、私もGoogle Chrome Beta版でLighthouseを走らせることが多いです)。

■デベロッパーツールからのLighthouse起動手順

  1. 評価対象のウェブサイトを前述したブラウザで開く
  2. ショートカットコマンド「Ctrl+Shift+I」でデベロッパーツールを開く
  3. デベロッパーツールメニューの「Lighthouse」タブを開く
    (表示されていない場合は「>>」をクリックしてメニューを展開し、「Lighthouse」タブを開く) f:id:uio8:20210903192914p:plain
  4. 「Generate report」を実行すると、現在開いているウェブサイトに対してLighthouse Report Viewerが実行される
  5. 結果が表示されるまでしばらく待つ

Google Chromeなどに拡張機能をインストールして使用する方法

PC版のGoogle Chrome、Microsoft Edge、VivaldiなどChromiumベースのブラウザであればGoogle Chromeと同様にLighthouseの拡張機能をインストールできます。
ブラウザで評価対象のウェブサイトを表示し、Lighthouse拡張機能からLighthouse Report Viewerを実行するのがこの方法です。
ただ、前述のデベロッパーツールとは異なるバージョンのLighthouse Report Viewerが実行されることがあります
より新しいバージョンのLighthouse Report Viewerで検証したい場合は前述のGoogle Chrome Beta版でデベロッパーツールを使用する方がいいでしょう。

■拡張機能からのLighthouse起動手順

  1. Chrome拡張機能をサポートしているブラウザで「Lighthouse - Chrome ウェブストア」のページを開き、「Chromeに追加」からLighthouse拡張機能をインストールする。
  2. 評価対象のウェブサイトをブラウザで開く
  3. ブラウザのアドレスバーの右側などの拡張機能一覧からインストールしたLighthouseをクリックする
    f:id:uio8:20210903193029p:plain
  4. 「Generate report」を実行すると、現在開いているウェブサイトに対してLighthouse Report Viewerが実行される
  5. 結果が表示されるまでしばらく待つ

Lighthouse Report ViewerのURLに直接入力して起動する方法

この方法は前述したLighthouseの拡張機能がリクエストを送るURLを直接使用する方法です。
拡張機能をインストールする必要がなく、SafariなどのLighthouse拡張機能をサポートしていないブラウザやスマートフォンでも実行できます。

■Lighthouse Report Viewer URLからのLighthouse起動手順

  1. 次のURLの<検証するURL>の箇所に評価対象のウェブサイトのURLを入力してブラウザで表示させる。
    https://googlechrome.github.io/lighthouse/viewer/?psiurl=<検証するURL>&strategy=mobile&category=performance&category=accessibility&category=best-practices&category=seo&category=pwa&utm_source=lh-chrome-ext
    
  2. 結果が表示されるまでしばらく待つ

Lighthouse Report Viewerでの結果画面

Lighthouse Report Viewerを実行してしばらくするとPerformance、Accessibility、Best Practices、SEOのカテゴリについての採点結果とPWAの対応可否が表示されます。
評価の結果、何が問題であるかは結果画面の下部へ各カテゴリごとに対策のヒントが表示されるようになっているので、その対策を調査しながら対応をしていきます。
次の画像はカテゴリの採点が一部100点を達成できなかった場合のLighthouse Report Viewer結果画面です。対策途中では、ほとんどの場合にこのシンプルな画面を見ることになります。

Lighthouse Report Viewerで100点を達成できなかった場合
Lighthouse Report Viewerで100点を達成できなかった場合

Lighthouse Report Viewerでの検証→調査→対策を繰り返し実施して各カテゴリの採点がすべて100点となり、PWAの基準も満たすと黒背景に花火ドーンとなります。
ありがちなアニメーションですが、それまでの作業過程で苦労があるほど内心嬉しくなってしまう粋な演出です。
(つい最近の仕様変更で花火は上がらなくなったようです。。。)

Lighthouse Report Viewerで100点を達成できた場合
Lighthouse Report Viewerで100点を達成できた場合

余談ですが、私のウェブサイトの場合、画像の容量圧縮対応はした一方で画像サイズだけは縮小していないこともあって、Performanceカテゴリで100点満点になるのは十数回に1回程度です。
試行する回によって評価点が変動するのはLighthouse Report Viewerからウェブサイト間の回線速度が評価に影響しているからかもしれません。
画像サイズを縮小していない理由は今後、モバイル端末の回線が5Gになり高速化することで画像表示のパフォーマンスへの影響が低減されていく過程を定点観測したいと考えたからです。

Lighthouseで高得点を出すために配慮したこと

私が今回試したのはindex.htmlのみの軽量ウェブサイトなので最初からある程度の得点は出ていました。
ただ、それでもここは配慮しておかないとLighthouseで高得点を出すのは難しいというポイントを挙げておきます。

  • レスポンシブ対応
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />やスタイルシートの@mediaなどを使用してPCの大画面からApple Watch程度の小画面まで柔軟に表示できるようにしました。
    レスポンシブが実際に対応できたかは、Google Chromeのデベロッパーツールで左上のスマートフォンとタブレットのマークをクリックし、デバイスツールバーを有効にして各種スマートフォンやカスタムデバイス(Apple Watchやガラホのサイズを入力)を指定して確認していました。

    f:id:uio8:20210903192944p:plain

  • 文字の大きさと色
    文字の大きさはモバイルを考慮して17px以上にしました。当初は16pxにしていましたが、「Google Search Console」の方で「テキストが小さすぎて読めません」のエラーがでたため、17pxに変更することでクリアしました。
    文字色は背景色とのコントラスト比4.5以上となるように配色を選択しました。
    コントラスト比は文字の太さにも関係しているため、次のツールを使用して背景色、文字の太さを考慮しながらコントラスト比4.5以上となる文字色を決めていきました

    WebAIM: Contrast Checker

  • 画像の軽量化
    最近では様々な画像圧縮ツールがあるので、それらを使用して画像圧縮を試して最も圧縮効果があった画像を採用しました。
    ただ、前述のように私の場合はサイズの縮小はしなかったため、完全にLighthouseのレポートに従ったわけではありません。

Lighthouseで高得点を目指すことはサイトパフォーマンスの観点では良いことですが、注意するべきなのは前述したようにLighthouse Report Viewerはあくまでサイトパフォーマンス測定ツールなので、ウェブサイトが有用なコンテンツを発信しているかは評価していない点です。
そのため、Lighthouse Report Viewerの評価を元に十分対策をとって100点が取れなくても、必要なコンテンツ(譲れないコンテンツ)を削ることはするべきではないと私は考えています。
ウェブサイトにはそれぞれコンテンツを配信する目的があるため、その目的を達成することが最優先であり、サイトパフォーマンスはそのコンテンツ配信を最適化するものだということを忘れないようにしたいと思っています。
Lighthouse Report Viewerで評価をしていると高得点のために必要なコンテンツまで削るという選択肢が頭に浮かんできたことがあるので、自分への備忘のためにもこのことを記載しておきました。

まとめ

今回はindex.htmlのみの簡単なウェブサイトにPWAを導入して、Lighthouse Report Viewerの各カテゴリーのスコアが可能な限り100点に近づけるように対策を実施してみました。
この過程でウェブサイトのPWAへの対応方法がわかったのは大きな収穫ですが、それに加えて最も効果が大きいと感じたのはLighthouse Report Viewerによる監査と指摘事項の修正の繰り返しによって、ウェブサイトのSEO対策がその流れの中で学習および実施することができたことです。
SEOに関する知識も検証ツールも何もない状態でサイトの質を向上するのは知識の習得や調査に非常にコストがかかりますが、Lighthouse Report Viewerのレポートを参考にして対処方法を調査し、適用するということを繰り返すことが言わば道標のようになっていると感じられました。
そして、このように学習を進めながら試行錯誤を続けた結果、ウェブサイトのPWA対応の方法と昨今のSEO対策が部分的に理解でき、無駄にPWAに対応したサイト(自己紹介)ができました。

Hidekazu Konishi, ALL AWS Certifications Engineer

執筆者小西秀和

ALL AWS Certifications Engineer(AWS認定全取得)の知識をベースにAWSクラウドの活用に取り組んでいます。