PWA Night CONFERENCE 2020行って、ampproject/amp-sw: A drop in service worker library to help your AMP pages gain network resiliency in 1 line存在を知りました。

興味を持ったので、サンプルページを実装してみました。
実装時に気づいたことを記載します。


作成したページ

作成したページは以下になります。


ファイル構成

以下のファイルをサンプルとして作成しました。
amp-soundcloud で soundcloud で公開されている曲をページに埋め込んでいます。

ファイル名内容
index.htmlINDEX AMPページ
index2.htmlINDEX AMPページからリンクしているAMPページ
offline.htmlオフライン時に表示するページ
serviceworker.htmlServiceWorkerを読み込むIframeページ
sw.jsServiceWorker

AMP ページで ServiceWorker を使用するための実装内容

AMP ページで ServiceWorker を使用するための実装内容を説明します。

index.html

index.html内の実装について説明します。

  • metaタグ内に、以下のスクリプトを記載する。

     <script async custom-element="amp-install-serviceworker" src="https://cdn.ampproject.org/v0/amp-install-serviceworker-0.1.js"></script>     
    

  • amp-install-serviceworker タグで、ServiceWorker の JavaScript と、Iframeを指定する

      <amp-install-serviceworker src="/amppwa/sw.js"
          data-iframe-src="https://examples.monotalk.xyz/amppwa/serviceworker.html"
          layout="nodisplay">
      </amp-install-serviceworker>  
    

serviceworker.html

serviceworker.html の実装は以下の通りです。

<!doctype html>
<html>
    <head>
        <title>installing service worker</title>
        <script type="text/javascript">
            var swsource = "https://examples.monotalk.xyz/amppwa/sw.js";
            if("serviceWorker" in navigator) {
                navigator.serviceWorker.register(swsource).then(function(reg){
                    console.log('ServiceWorker scope: ', reg.scope);
                }).catch(function(err) {
                    console.log('ServiceWorker registration failed: ', err);
                });
            };
        </script>
    </head>
    <body>
    </body>
</html>

var swsource = "https://examples.monotalk.xyz/amppwa/sw.js"; では自サイトのServiceWorkerのパスを指定する必要があります。

sw.js

sw.js以下の実装を行いました。

importScripts('https://cdn.ampproject.org/sw/amp-sw.js');
AMP_SW.init({
    linkPrefetchOptions: {
        maxAgeSecondsInCache: 1000
    },
    offlinePageOptions: {
        url: '/amppwa/offline.html',
        assets: [],
    }
});

最小限の実装1line場合は以下のようになります。

importScripts('https://cdn.ampproject.org/sw/amp-sw.js');
AMP_SW.init();


amp-sw の挙動について

amp-swソースと、README.md実際の動作から どのような挙動をするのか確認してみました。
以下、気づいたことを記載します。

ユースケースについて

以下は、README.md記載されていました。

  • httpレスポンスヘッダの設定よりも長い期間、stale-while-revalidateAMPスクリプトをキャッシュする。

  • 有効な表示済みのAMPドキュメントをキャッシュし、不安定なネットワーク状態の場合にのみ表示する。

  • 指定したCache戦略でページにとって重要なアセットをキャッシュする。

  • ユーザーアクセスしたことのないページに移動したときのために、オフラインページをキャッシュする。

  • AMPページからの発信リンクをprefetchする。

5つのモジュールに分かれている

以下、5つのモジュールに分かれています。

  • AMP caching module
    AMP の JavaScript 等のリソースをcacheするモジュールです。

  • Document caching module
    AMP の HTML をcacheするモジュールです。

  • Asset caching module
    画像/フォントなどの静的アセットをcacheするモジュールです。

  • Offline page module
    表示したことのないページをオフライン時に表示した際、offline用のHTMLを表示するモジュールです。

  • Link prefetch module
    link rel=prefetchサポートしないブラウザにプリフェッチ機能を提供するモジュールです。
    AMPのページは、Cacheされると、link rel=prefetch記述は削除されてしまいます。
    CacheされたHTMLでもprefetchの機能を実現するために提供されていると考えられます。

AMP_SW.init() の実装

AMP_SW.init() のソースは以下になります。

function init(config: ServiceWorkerConfiguration = {}) {
  ampCachingModule.init();
  let fallbackOfflinePageUrl;
  if (config.offlinePageOptions) {
    fallbackOfflinePageUrl = config.offlinePageOptions.url;
  }
  const navRoute = documentCachingModule.init(
    config.documentCachingOptions,
    fallbackOfflinePageUrl,
  );
  if (config.assetCachingOptions) {
    import(/* webpackChunkName: "optional-modules" */ '../asset-caching/index').then(
      async ({ AssetCachingAmpModule }) => {
        const assetCachingModule = new AssetCachingAmpModule();
        await assetCachingModule.init(
          config.assetCachingOptions as AssetCachingOptions,
        );
      },
    );
  }

  if (config.linkPrefetchOptions) {
    import(/* webpackChunkName: "optional-modules" */ '../link-prefetch/index').then(
      async ({ LinkPrefetchAmpModule }) => {
        const linkPrefetchModule = new LinkPrefetchAmpModule();
        await linkPrefetchModule.init(
          config.linkPrefetchOptions as LinkPrefetchOptions,
          navRoute,
        );
      },
    );
  }

  if (config.offlinePageOptions) {
    import(/* webpackChunkName: "optional-modules" */ '../offline-page/index').then(
      async ({ OfflinePageAmpSwModule }) => {
        const offlinePageModule = new OfflinePageAmpSwModule();
        const offlinePageConfig: OfflinePageOptions = config.offlinePageOptions as OfflinePageOptions;
        await offlinePageModule.init(
          offlinePageConfig.url,
          offlinePageConfig.assets,
        );
      },
    );
  }

  // Taking over the document
  self.addEventListener('install', function(e: ExtendableEvent) {
    const { skipWaiting } = self as ServiceWorkerGlobalScope;
    e.waitUntil(skipWaiting());
  });

  self.addEventListener('activate', async (e: ExtendableEvent) => {
    const { clients } = self as ServiceWorkerGlobalScope;
    e.waitUntil(
      clients.claim().then(async () => {
        // Cache current document if its AMP.
        const windowClients = await clients.matchAll({ type: 'window' });
        return Promise.all(
          documentCachingModule.cacheAMPDocument(windowClients),
        );
      }),
    );
  });
}

ソースを見る限り、以下のモジュールはデフォルトで動作します。

  • AMP caching module
  • Document caching module

以下はオプション指定により動作します。

  • Asset caching module
  • Offline page module
  • Link prefetch module

サンプルを動かして気づいたこと

  • AMP というプレフィックスがついたCacheが作られる
    それぞれのモジュールに対応するAMPというプレフィックスがついた名前でCacheが作られます。
    2020-02-22 23.35.27.png - Google ドライブ

  • Link prefetch module は動作しない?
    data-rel='prefetch'つけたリンクをCache してくれるようなのですが、特に動作している雰囲気はありませんでした。
    Google に Cacheされたページの場合動作するのかもしれません。


参考

以下、参考にした記事になります。

offline用のページを用意する発想はなかったのですが、アクセシビリティ観点でも有用に思いました。
自サイトのPWAに別途組み込んで見ようかと思います。
以上です。

コメント