AMP in PWA Google公式チュートリアルの日本語訳

## プログレッシブウェブAMP
この記事はGoogle Developersの[Progressive Web AMPs](https://codelabs.developers.google.com/codelabs/amp-in-pwa/#0)の日本語訳です。Progressive Web AMPsの多分**一番いい**チュートリアルです。
しかし、Service Worker, Promise, AMPに関して基礎的な知識がないと途中で挫折することになると思います。
そのような場合はこの記事をストック(←重要)した後、以下の参考記事を読むことをおすすめします。

- そもそもプログレッシブウェブAMPって何？って方へ: https://www.suzukikenichi.com/blog/pwamp-is-combination-of-pwa-and-amp/

- Service Workerって何？って方へ: https://developers.google.com/web/fundamentals/getting-started/primers/service-workers?hl=ja
- Promiseって何？って方へ: http://azu.github.io/promises-book/
- AMPって何？って方へ: https://www.ampproject.org/ja/learn/about-amp/

記事内の「※」付きのコメントは私が加えた補足です。

### Twitter
Google, Web関連を中心にソフトウェアの情報をキャッチアップしがちです。
Twitter: https://twitter.com/awjecc  

<img width="512" alt="20170317194903.png" src="https://qiita-image-store.s3.amazonaws.com/0/29792/25c16e9b-216d-2983-4234-f9120b36062a.png">


## 1. 導入
AMPは、高速にレンダリングされる静的コンテンツ用のページを作成する方法です。プログレッシブWebアプリケーションは、Web上で信頼性が高く、迅速で魅力的な経験を提供します。開発者がAMPを使用してWebサイトでPWAを構築するには、いくつかの方法があります。

### この記事で実装するもの
このCodelabでは標準AMPサイトをプログレッシブWebアプリケーションに変換して、さらにAppShellを使用して強化することによって、2つの異なるAMP + PWAパターンを実装する方法を説明します。

### この記事で学ぶもの
AMPサイトをプログレッシブWebアプリケーションに変換する方法
App Shellを使用して経験を向上させる方法

### 必要なもの

- 最新のバージョンのChrome
- Node.js
- シンプルなコード
- テキストエディタ
- HTML, CSS, JavaScript, そしてChromeの開発ツールに関する基本的な知識

このcodelabは、AMPとプログレッシブWebアプリケーションに焦点を当てています。それと関連性の低いコードは、簡単にコピー＆ペーストできるように表示されます。


## 2. セットアップ

### コードをダウンロードする
[ダウンロード](https://github.com/googlecodelabs/amp-pwa/archive/1.0.zip)をクリックするか、以下のGithubからプロジェクトをクローンしてください。

```
git clone https://github.com/googlecodelabs/amp-pwa.git
```
### Node.jsをインストールする
このcodelab全体で、Service Woker の生成を支援するツールを使用します。
このツールはsw-precacheと呼ばれ、これを動かすにはNode.jsをインストールする必要があります。
OSにNode.jsをインストールする方法については、Node.jsの[Webサイト](https://nodejs.org/ja/)を参照してください。

### localhostで稼働する
このcodelabでは、ローカルファイルシステムのコンテンツを提供できる必要があります。
そのために[serve](https://www.npmjs.com/package/serve)というNode.jsベースの静的コンテンツ・サーバーを使用します。
インストールするには、次のコマンドを実行します。

```
npm install -g serve
```

### 初期のコードを実行する
すべての部分が適切に配置されていることを確認します。
コンソールで、codelabソースコードがインストールされているディレクトリに移動し、その中の/workフォルダに移動します。
これは、このcodelab全体で使用するフォルダになります。workフォルダで`serve`コマンドを実行するだけでサーバーを起動できます。

![serveコマンドでサーバ起動](https://codelabs.developers.google.com/codelabs/amp-in-pwa/img/e679038458b51e52.png)
※ コマンドが見つからない場合はShell を再起動してみてください

Chromeで[http://localhost:3000](http://localhost:3000)を開き、AMPインデックスページが表示されているかどうかを確認します。  
※ 私の環境ではデフォルトでhttp://localhost:5000 でした

## 3. PWAの構成要素
※ PWAはProgressive Web Appsの略

- プログレッシブ - ブラウザにかかわらず、すべてのユーザーのために機能する。
- 応答性 - デスクトップ、モバイル、タブレットなどあらゆる要因に適合します。
- 接続に依存しない - オフラインまたは低品質のネットワークで作業するようにService Woker で強化する。
- App-like - App shellで構築されているため、アプリのようなの操作感やナビゲーションでユーザーにアプリみたいだと感じさせます。
- フレッシュ - Service Woker のアップデートプロセスのおかげで、常に最新の状態を保ちます。
- 安全 - スヌーピングを防止し、コンテンツが改ざんされていないことを保証するために、HTTPS経由で提供します。
- 発見しやすさ - W3CのマニフェストとService Woker の登録範囲のおかげで、検索エンジンがそれを「アプリケーション」として識別できます。
- 再訪問のしやすさ - プッシュ通知などの機能を使用して再訪問を容易にします。
- インストール可能 - ユーザーがアプリストアからインストールする手間をかけずにアプリをホーム画面に「置ける」ようにします。
- リンク可能 - URL経由で簡単に共有でき、複雑なインストールを必要としません。

## 4. Service Woker の追加
まず、アプリケーションのロゴイメージやフォントなどの静的コンテンツをキャッシュするService Woker を追加します。  
service woker の作成を大幅に簡素化するJeffrey Posnickによって作成されたツールである`sw-precach`をインストールする

```
npm install -g sw-precache
```

`work`フォルダの直下に`sw-precache-config.js`というファイルを作り、以下のコードを追記します。

```sw-precache-config.js
module.exports = {
  staticFileGlobs: [
    'img/**.*'
  ]
};
```

これにより`sw-precache`が設定され、imagesディレクトリ以下のすべてのファイルとfontsディレクトリのすべてのファイルをキャッシュするService Wokerを作成します。

`sw-precacheを実行します。

```
sw-precache --config=sw-precache-config.js
```

※ コマンドが見つからない場合はShell を再起動してみてください

イメージとフォントをキャッシュするように設定された`service-worker.js`ファイルが生成されます。
これらのフォルダで何かが追加または変更されるたびに、前のsw-precacheコマンドでService Woker を再生成する必要があります。

service woker の生成は、タスクの半分に過ぎません。
次のステップは、AMPページからService Woker をインストールすることです。
このために[amp-install-serviceworker](https://www.ampproject.org/docs/reference/components/amp-install-serviceworker)というものが提供されています。

各AMPページの`</head>`タグの直前にService Woker Javascriptを追加して読み込みます。  
※ work/index.htmlに追加すれば良いです。

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

各AMPページの`</body>`タグの直前に以下の設定を追加する必要もあります。
※ work/index.htmlに追加すれば良いです。

```
<amp-install-serviceworker
  src="/service-worker.js" 
  layout="nodisplay">
</amp-install-serviceworker>
```

### AMPキャッシュからservice woker をインストールする
AMPキャッシュからjavascriptファイルを使用してservice woker をインストールすることはできません。
その代わり、`amp-install-serviceworkerb`には、そのURLをiframeとしてロードし、AMPキャッシュからのService Woker のインストールを可能にする`data-iframe-src`という追加の属性があります。

workフォルダの直下に`install-service-worker.html`ファイルを作成し次の内容を追加します。

```install-service-worker.html
<!doctype html>
<html>
  <head>
    <title>installing Service Woker</title>
    <script type="text/javascript">
        var swsource = "/service-worker.js";
        if("serviceWorker" in navigator) {
          navigator.serviceWorker.register(swsource)
            .then(function(reg){
              console.log('SW scope: ', reg.scope);
            })
            .catch(function(err) {
              console.log('SW registration failed: ', err);
            });
        };
    </script>
  </head>
  <body>
  </body>
</html>
```

このページがiframeの内側から読み込まれると、このコードはService Woker の登録をします。
AMPファイルの`amp-install-serviceworker`に戻り、このページへの参照を追加します。

`amp-install-serviceworker`の設定は、次のようになります。
※ work/index.htmlの`</body>`の直前記述を書き変えます。

```
<amp-install-serviceworker 
  src="/service-worker.js" 
  layout="nodisplay"
  data-iframe-src="/install-service-worker.html">
</amp-install-serviceworker>
```

### 動作確認
Chromeでhttp://localhost:3000を開きます。
Chromeのメニュー > その他のツール > デベロッパーツールでツールを開きます。
Chromeデベロッパーツール内で、[アプリケーション]タブに移動し、[Service Woker]項目をクリックします。

![デベロッパーツール Service Woker](https://codelabs.developers.google.com/codelabs/amp-in-pwa/img/fbd33b752125fb70.png)

上記のように、Service Woker がインストールされていることを確認できればオッケーです。

### おめでとうございます！
静的コンテンツ(imagesフォルダ以下のすべてのファイルとフォントファイル)をキャッシュするService Woker を作成してインストールすることで、
AMPサイトをプログレッシブWebアプリケーションに変換するための第一歩を踏み出しました。

## 5. コネクティビティから独立させる

### 訪問したページをキャッシュする
ユーザーが訪問したページをキャッシュするには、`sw-precache-config.js`に`sw-precache`の別の設定を追加する必要があります。
以下のコードを`staticFileGlobs`属性の直後に追加します。

```
runtimeCaching: [{
  urlPattern: '',
  handler: (request, values, options) => {
    // If this is NOT a navigate request, such as a request for
    // an image, use the cacheFirst strategy.
    if (request.mode !== 'navigate') {
      return toolbox.cacheFirst(request, values, options);
    }

    // If it's a navigation request, use the networkFirst strategy.
    return toolbox.networkFirst(request, values, options);
  }
}]
```

※ 直前の行の`]`を`],`に書き換えないとエラーが出ます。  
※ `sw-precache --config=sw-precache-config.js`を実行すると`service-worker.js`が更新されます。  
※ キャッシュを扱っているのでシークレットブラウザで開くことをおすすめします。  

`runtimeCaching`属性は、ハンドラ構成の配列を受け取ります。
このリクエストは(※ ページもしくは画像などのリソース構わず)すべてのリクエストを受け取るので、
`request.mode`属性を使用して、ページまたはリソースのリクエストかどうかを確認し、
それぞれに異なるキャッシュ戦略を使用しています。

### オフラインページを追加する
今、オフラインで以前に訪問したページにアクセスすると、そのページのキャッシュを見ることができます。
しかし、以前訪問していなかったリンクをユーザーがクリックすると、オフラインページが表示されます。
service woker を使用すると、オフラインページをカスタマイズできます。

workフォルダ内に`offline.html`ファイルを作成します。シンプルなオフラインページ

```
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>The Photo Blog - Offline</title>
  <meta name="viewport"
        content="width=device-width,minimum-scale=1,initial-scale=1">
</head>
<body>
  <h1>You are Offline</h1>
</body>
</html>
```

`sw-precache-config.js`を編集して、`sw-precache`設定を更新します。
オフラインページをキャッシュするためにstaticFileGlobsのリストにオフラインページを追加します。

```
module.exports = {
  staticFileGlobs: [
    'img/**.*',
    'offline.html'
  ],
...
```

すでに、`toolbox.networkFirst`を使って、Promiseを返すコードを書きました。
しかし、ネットワークが使用できず、ファイルがキャッシュに見つからない場合、このPromiseは拒否されてしまいます。
リクエストが失敗したときにオフラインページを返すために`sw-precache-config.js`の`toolbox.networkFirst`メソッドコールの後にcatchセクションを追加します：

```sw-precache-config.js
...
if (request.mode !== 'navigate') {
  return toolbox.cacheFirst(request, values, options);
}

// If it's a request for content, use the networkFirst
// strategy, and send an offline page if both network and
// cache fail.
return toolbox.networkFirst(request, values, options)
  .catch(() => {      
    return caches.match('/offline.html', {ignoreSearch: true});
  });      
...
```

### AMPランタイムのキャッシュ

この時点で、キャッシュ・ページの1つをオフラインで開こうとすると、コンソール上に一連のエラーが発生したページが表示されます。
![offline errors](https://codelabs.developers.google.com/codelabs/amp-in-pwa/img/eb02a8d184fcf3fb.png)

ページおよびサポートするコンテンツはキャッシュされていましたが、AMPランタイムはキャッシュされていなかったためです。
私たちの`sw-precache`の設定をアップデートして、AMPランタイムも処理してみましょう。次のコードを`runtimeCaching`の配列に追加します。
```
...
{
  urlPattern: /cdn\.ampproject\.org/,
  handler: 'fastest'
}
...
```
これは、AMPランタイムを`fastest`戦略でキャッシュします。この戦略は、キャッシュとネットワークの両方からリソースを並行してリクエストします。
キャッシュされたバージョンが、通常使用可能な場合は返されます。
しかし、並列ネットワークリクエストは、キャッシュをAMPランタイムの最新バージョンに更新します。それは次回のリクエスト時に返されます。

この時点での、完全な`sw-precache-config.js`は次のようになります。

```
module.exports = {
  staticFileGlobs: [
    'img/**.*',
    'offline.html'
  ],
  runtimeCaching: [{
    urlPattern: '*',
    handler: (request, values, options) => {
      // If this is NOT a navigate request, such as a request for
      // an image, use the cacheFirst strategy.
      if (request.mode !== 'navigate') {
        return toolbox.cacheFirst(request, values, options);
      }

      // If it's a request for content, use the networkFirst
      // strategy, and send an offline page if both network and
      // cache fail.
      return toolbox.networkFirst(request, values, options)
        .catch(() => {      
          return caches.match('/offline.html', {ignoreSearch: true});
        });      
    }
  }, {
    urlPattern: /cdn\.ampproject\.org/,
    handler: 'fastest'
  }]
};
```

忘れずにService Woker を再生成しましょう。

```
sw-precache --config=sw-precache-config.js
```

### 動作確認
http://localhost:3000 を開きます。
※ 私の環境だとhttp://localhost:5000 でした

Chromeデベロッパーツールを開き、[Application]タブに移動します。 
「Cache」セクションで、「Cache Storage」を右クリックし、「Refresh」をクリックします。
キャッシュをチェックし、ページに必要なファイルがキャッシュにリストされているかどうかを確認します。
![Service Woker キャッシュ](https://codelabs.developers.google.com/codelabs/amp-in-pwa/img/6f4a32fe8ac8d05b.png)

[Service Woker]セクションで、[Offline]チェックボックスをクリックし、ページをリロードします。オフラインでもちゃんと読み込まれるはずです。そのまま記事へのリンクの1つをクリックしてください。記事がキャッシュされていないので、カスタムオフラインページが表示されます。

## Webマニフェストを追加する
PWAの大きな利点の1つは、ホーム画面に追加できることです。そのためには、アプリケーションにWebマニフェストを追加する必要があります。

これは、この執筆時点で、ホームスクリーンにアプリを追加できるようにするためのマニフェスト属性です：

- ホームスクリーンランチャーに必要な`name`と`short_name`の両方。
- `theme_color`と`background_color`の両方は、スプラッシュ画面の作成に使用されます。
- `display`はネイティブアプリの見た目と感覚を提供するためにスタンドアロンに設定する必要があります。
- アイコンの最小サイズは192pxです。 アイコンはホーム画面と起動したスプラッシュ画面に必要です。
- ユーザーがホームスクリーンアイコンをクリックしたときに最初に開かれる`start_url`。

### Webマニフェストの生成
Webマニフェストを手作業で書くと、エラーを起こしやすいです。この作業に役立つオンラインマニフェストジェネレータがいくつかあります。私たちはhttps://app-manifest.firebaseapp.com/ にあるものを使用してwebマニフェストを生成します。

フォームフィールドに必要な値を入力します。このcodelabでは、以下の値を使用します。

- App Name: AMP PWA Codelab
- Short Name: AMP PWA Codelab
- Theme Color: #00796b
- Background Color: #00796b
- Display Mode: Fullscreen
- Start URL: /index.html

次のステップはアイコンをジェネレーターにアップロードすることです。 
ICONボタンをクリックし、workフォルダに移動し、`/icons/web_hi_res_512.png`にアイコンをアップロードします。
![Web App Manifest Generator](https://codelabs.developers.google.com/codelabs/amp-in-pwa/img/ed85d45b529a88c8.png)

GENERATE ZIPボタンをクリックして、複数のサイズのアイコンとフルマニフェストをダウンロードできます。
ダウンロードしたzipファイルを展開して、`manifest.josがworkフォルダの直下に来るようにコピーします。

### マニフェストファイルを指定する

ブラウザにマニフェストを認識させるには、それぞれのページにリンクさせる必要があります。
linkタグをそれぞれのページのheadタグ内に追加することで完了します。

```
<link rel="manifest" href="/manifest.json">
```

### `start_url` がキャッシュされていることを確認する

ホームスクリーンで動くようにするために必要な最後の条件は、ユーザーがオフラインのときでも、start_urlで参照されるURLを常に使用可能にする必要があることです。
この条件を満たすために、Service Woker がインストールされるときにURLをキャッシュしておき、
さらに、`networkFirst`キャッシュ戦略を使用して常に最新のものにする事ができます。


オフラインページと比較すると、`index.html`ページはより動的な性質を持つため、別のアプローチを使用してページをプリキャッシュする必要があります。`sw-precache`の拡張性を利用します。
`sw-precache-config.js`ファイルで、別の設定変数を追加します。

```
importScripts: ['service-worker-import.js']
```

これにより、生成されたService Woker へ`importScript`callが追加されます。次に、ルートフォルダに`service-worker-import.js`ファイルを作成し、次のコードを追加します。

```
toolbox.precache(['/index.html']);
```

インポートされたスクリプトが生成されたService Woker によって呼び出されると、`index.html`ページがランタイムキャッシュに追加されます。
こうすることで、以前作成した既存のハンドラでページを使用できるようになります。

Service Woker の更新

```
sw-precache --config=sw-precache-config.js
```

### 動作確認
Chromeデベロッパーツールで、[Application]タブに移動し、[Application]セクションの[Manifest]項目を確認します。マニフェストに含まれる情報は、右側に表示されます。
![webマニフェスト](https://codelabs.developers.google.com/codelabs/amp-in-pwa/img/10c314274cc26177.png)

### Extra Credits
`index.html`ページはキャッシュされていますが、記事の画像はキャッシュされていないことに気づいたかもしれません。
コンテンツのプリキャッシュすることは可能で、しかしながら動的な`index.html`ページのどの画像がキャッシュされているかを把握することは難しいです。
可能な解決策は、画像のリクエストが不可能なときにオフラインイメージを提供することです。これは読者のエキササイズです(←※ ギャグなのか何か意味があるのかよくわからない)。

### AMP as a Canonical PWA

おめでとうございます、最初のAMP as a Canonical PWAを実装しました。
あなたはオフラインのシチュエーションにより強く、静的ファイルと画像のキャッシュによりローディングを最適化され、ホームスクリーンから起動する資格を持つアプリケーションを作りました！

## 7. App Shell の作成
AMP as a Canonical PWAでほとんどのシチュエーションでは十分ですが、開発者はAMPでまだサポートされていないプッシュ通知やクレデンシャルマネージャAPIなどの機能が必要になるかもしれない。

このようなシチュエーションでは、AMP in a PWAが使えます。

### Shellでリクエストを置き換える

まずApp Shellをアプリケーションに追加しましょう。service woker 一度インストールされると、ナビゲーションリクエストをキャッチしてShellで置き換えるため、Service Woker を変更します。

`shell.html`をworkフォルダ内に作ります。

```shell.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">  
    <title>AMP => PWA Demo</title>
    <style type="text/css">
body{margin:0;padding:0;background:#F5F5F5;font-size:12px;font-weight:300;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif}a{text-decoration:none;color:#000}.header{color:#fff;background:#1976D2;padding:8px 16px;box-shadow:0 2px 5px #999;height:40px;display:flex;align-items:center}.header h1{margin:0 8px 0 0}.header amp-img{margin-right:8px}.header img{margin-right:8px}.header a{color:#fff}.header a:visited{color:#fff} 
    </style>
  </head>
  <body>
    <header class="header">
      <img src="/img/amp_logo_white.svg" width="36" height="36" />
      <h1><a href="/">AMP PWA Codelab - PWA</a></h1>
    </header>    
    <div id="amproot">
      <!-- AMP Content should appear here! -->
    </div>
  </body>
</html>
```

`sw-precache-config.js`を更新して、作成した`shell.html`をナビゲートリクエストに対応させます。フル設定は次のようになります：

```
module.exports = {
  staticFileGlobs: [
    'img/**.*',
    'offline.html',
    'shell.html',
    'js/**.js'
  ],
  runtimeCaching: [{
    urlPattern: '*',
    handler: (request, values, options) => {
      // If this is NOT a navigate request, such as a request for
      // an image, use the cacheFirst strategy.
      if (request.mode !== 'navigate') {
        return toolbox.cacheFirst(request, values, options);
      }

      return caches.match('/shell.html', {ignoreSearch: true});  
    }
  }, {
    urlPattern: /cdn\.ampproject\.org/,
    handler: 'fastest'
  }],
  importScripts: ['service-worker-import.js']
};
```

メインハンドラが変更され、ナビゲーションリクエストがApp Shellに置き換えられました。

### `amp-shadow`を使ったAMPの読み込み
この手順では、workフォルダの`js/app.js`ファイルを使用します。
このファイルには、shellに必要な定型コードがすでに含まれています。
例えば、バックエンドからAMPドキュメントをフェッチする方法、およびamp-shadowコンポーネントがロードされたときに実行されるコードのスケジュールを可能にするPromiseが含まれています。

まず、App Shellのheadタグ内に`amp-shadow`コンポーネントを追加します。

```
<!-- Asynchronously load the AMP-Shadow-DOM runtime library. -->
<script async src="https://cdn.ampproject.org/shadow-v0.js"></script>    
```

Shell の一番下に`js/app.js`スクリプトをインポートします。

```
<script src="/js/app.js" type="text/javascript" defer></script>
```

次に、`app.js`スクリプトを開き、`<div id="amproot">`にAMPコンテンツを追加するために必要な変数を設定するコードを追加します。

```
const ampRoot = document.querySelector('#amproot');
const url = document.location.href;
const amppage = new AmpPage(ampRoot, router);
ampReadyPromise
  .then(() => {
    amppage.loadDocument(url);
  });
```

元の要求はservice woker によって`shell.html`のコンテンツに置き換えられましたが、URLは同じです。
AMP rootに挿入するには、このURLからコンテンツを取得する必要があります。そこで、`ampReadyPromise`がURLからコンテンツを解決して読み込むのを待ちます。

`loadDocument`メソッドを実装しましょう。前のコードの数行上のプレースホルダーを見つけて書き換えます。

```
loadDocument(url) {
  return this._fetchDocument(url)
    .then(document => {
      router.replaceLinks(document);
      window.AMP.attachShadowDoc(this.rootElement, document, url);            
    });       
}
```

ドキュメントはURLから取得され、一度解決されたら、ドキュメントにの取得のために`amp-shadow` APIが呼び出されます。

### AMPファイルの内容を操作する
`App Shell`にAMPページを挿入する場合、開発者はAMPドキュメントのセクションを追加、削除、または変更する必要がありそうです。

フェッチされたドキュメントは通常のDOMドキュメントなので、開発者は必要に応じてそれを操作できます。
下のスニペットでは、ヘッダが既にAppShell に存在し、重複してしまうため、AMPドキュメントからHeader要素を削除しています。

```app.js
loadDocument(url) {
  return this._fetchDocument(url)
    .then(document => {
      const header = document.querySelector('.header');
      header.remove();
      router.replaceLinks(document);
      window.AMP.attachShadowDoc(this.rootElement, document, url);            
    });       
}
```

### 動作確認
http://localhost:3000 を開きます。
最初にページを開くとき、service woker がまだインストールされていないので、ブラウザはAMPページを表示します。
ページをリロードすると、App Shellのバージョンがレンダリングされます。

## Service Woker をサポートしないブラウザ
今の実装では、service woker をサポートしていないブラウザでアクセスしたユーザーは、App Shell を体験をすることができません。
幸運なことに、`amp-install-serviceworker`はページ上のリンクをShell URLに書き換える`fallback`を提供します。

AMPページで、`fallback`属性を含めるように`amp-install-serviceworker`を更新します。

service woker をサポートしていないブラウザを使用しているユーザーがAMPファイル内のリンクをクリックすると、AMPランタイムはそのリンクをShell URLに置き換え、元のURLをフラグメントとして
`#href=<元のURL>`というフォーマットで追加する

したがって、`/articles/1.html`へのリンクは`/shell.html#%2Farticles%2F1.html`になります。
App Shell のコードでこの変更を認識させましょう。コンテンツの正しいURLを見つける方法を追加する  
※ `/shell.html#%2Farticles%2F1.html`は`/shell.html#href=%2Farticles%2F1.html`の間違いかな..?

```
...
function getContentUri() {
  const hash = window.location.hash;
  if (hash && hash.indexOf('href=') > -1) {          
    return decodeURIComponent(hash.substr(6));
  }
  return window.location;  
}
...
const ampRoot = document.querySelector('#amproot');
const url = getContentUri();
const amppage = new AmpPage(ampRoot, router);
```

ユーザーがservice woker から来ている場合は、URLフラグメントが存在するため、`decodeURIComponent`を使用して最終URLを抽出できます。
URLの内容がService Woker に置き換えられていた場合、URLはすでに正しいです。
url変数への代入は、新しいメソッドへの呼び出しで置き換えられます。

### 壊れたURLの修正
URL書き換えフォールバックを使用することの副作用は、ユーザーがShell 内のリンクをクリックすると、URLバーのURLにフラグメントのShell URLが表示されることです。
ページ履歴APIを使用してそれを修正することができます。

```
ampReadyPromise.then(() => {
  return amppage.loadDocument(url);
})
.then(() => {
  if (window.history) {
    window.history.replaceState({}, '', url);
  }
});
```

loadDocument Promiseが正常に解決されると、Shell URLを読み込まれたばかりのものに置き換えます。


### 動作確認

service woker をサポートしていないブラウザを起動します。この記事の執筆時点では、WindowsのエッジとOSXのSafariなどがあります。
http://localhost:3000 を開きます。
最初はAMPページが開かれるはずです。ブラウザで開発者ツールを開いて、ソースコードを確認することで確認できます。
ここで、最初のページのリンクの1つをクリックします。 URLは書き換えられていて、ページはAppShell を使用してレンダリングされているはずです。

### おめでとうございます！
初めてのAMP in a PWAの実装が完了しました。あなたのアプリケーションはAMP as a Canonical PWAパターンのすべての利点を持っており、AMPでのみ可能だった機能を追加できるようになりました。

### What's Next?
このcodelabは、Progressive Web Apps with AMPを構築するときにできることの表面を引っ掻いただけです。
より複雑な体験を構築する場合は、アプリケーション設計を[PWA Checklist](https://developers.google.com/web/progressive-web-apps/checklist)と照合して確認してください
また、service woker の仕組みの詳細については、[Your First Progressive Web App](https://codelabs.developers.google.com/codelabs/your-first-pwapp/#0)を確認してください。