カバー画像 AMP Showcase at Ameba

アメブロでは2016年2月にAMPページの提供を開始し、リリース以降いくつかの改善を重ねてきました。そして2017年、できるだけオリジナルサイトのユーザー体験を再現できるようにAMPページをアップデートしました。本記事ではその一例をご紹介します。

LazyLoad With <amp-list>

オリジナルサイトではブログ本文に関係ないページ下部の要素をLazyLoadし、表示速度を改善しています。AMPページでは<amp-list>を使用して同様のことを実現しています。

<amp-list>ではXHRでデータを取得し、描画します。他の多くのAMPエレメントと同じように、スクロールに応じて画面表示領域に近付いた時にデータ取得・表示処理をおこなうので、LazyLoadを簡単に実装できます。

スクリーンショット LazyLoad with <amp-list>

ページ下部のモジュール群はスクロールに応じて遅延表示されます。

<amp-list>src属性には、データを取得するAPIのURLを指定します。データは配列で返却する必要があり、JSONのプロパティ名をitems属性で指定します。表示内容は<amp-mustache>テンプレートを使ってMustache形式で記述できます。

<amp-list
  layout="fixed-height"
  src="https://topics.api...."
  height="120"
  width="auto"
  items="topics"
>
  <div fallback>データが取得できませんでした。</div>
  <template type="amp-mustache">
    <a href="{{target_url}}">
      <amp-img src="{{img_url}}" width="80" height="80" alt="サムネイル画像 {{title}}"></amp-img>
      <p>{{title}}</p>
    </a>
  </template>
</amp-list>

CORS

AMPで利用するAPIは、クロスオリジンリクエストに配慮する必要があります。従来のCORSヘッダー(Access-Control-Allow-Origin, Access-Control-Allow-Credentialなど)に加え、以下のようなレスポンスヘッダーを実装します。

Access-Control-Allow-Origin: <origin>
AMP-Access-Control-Allow-Source-Origin: <source-origin>
Access-Control-Expose-Headers: AMP-Access-Control-Allow-Source-Origin

詳しい仕様はCORS Requests in AMPをご覧ください。

Search Bar With <amp-bind>

アメブロでは<amp-bind>を使って、オートサジェスト機能がついたSearch barを作成しました。<amp-bind>を使うと、AMPページ内で状態管理をすることができます。

スクリーンショット Search Bar with <amp-bind>

入力した文字列に応じて推薦ワードが表示されます。

まず、<amp-state>で状態(state)の初期値を設定します。

<amp-state id="searchState">
  <script type="application/json">
    { "autosuggest" : "" }
  </script>
</amp-state>

次に、input要素の入力に応じてAMP.setState()メソッドで状態を更新します。イベントはon=”イベント名:処理”のような形式で設定できます。今回は、input要素の変化時のイベントであるinput-debouncedを利用しています。

その他のイベントは、Actions and Events in AMPで説明されています。

<input
  class="search-bar__input"
  type="search"
  name="q"
  placeholder="ブログを検索"
  tabindex="0"
  on="input-debounced:AMP.setState({ searchState: { autosuggest: event.value }})"
/>

変更されたsearchState.autosuggestの値に応じて、<amp-list>によるXHRが発生し、その結果により画面を書き換えます。変更された値を使う属性は[src]のように[]で囲い、その中には従来のJavaScriptの式で文字列をつなぎます。こうすると、状態が更新されるたびに、APIリクエストが発生し、表示が切り替わります。

<amp-list
  layout="fixed-height"
  src="https:/api/autosuggest?q="
  [src]="'https://api/autosuggest?q=' + searchState.autosuggest"
  items="."
  credentials="include"
  height="225"
  width="auto"
>
  <template type="amp-mustache">
    <a
      class="search-result__anchor"
      href="https://search.ameba.jp/search.html?q={{.}}">{{.}}
    </a>
  </template>
</amp-list>

Modals With <amp-lightbox>

また、Search barには、モーダル表示として<amp-lightbox>も同時に使われています。オーバーレイとなる要素のタップでlightboxもクローズできるようにしています。

<amp-lightbox
  id="search-lightbox"
  layout="nodisplay"
  scrollable
>
  <nav class="search" role="dialog">...</nav>
  <button
    on="tap:search-lightbox.close"
  >Close</button>
  </div>
</amp-lightbox>

Like Buttons With <amp-bind> And <amp-form>

<amp-bind>を応用することにより、AMPページでもオリジナルサイトのように記事へのいいね!(Like)リアクションをすることができるようになりました。

スクリーンショット LIKE BUTTONS WITH <AMP-BIND> AND <AMP-FORM>

AMPページ上でいいね!アクションができるようになりました。

まず、いいね!ボタンがタップされた時点で、現在のステータスを取得します。<amp-state>src属性で、APIからデータを取得することもできます。

<amp-state
  id="iineState"
  src="https://iine.ameba.jp/status..."
  credentials="include"
></amp-state>

取得できたステータスに応じて、いいね!を実行する、もしくはキャンセルするボタンの表示を出し分けします。表示の出し分けは、iineStateの状態に応じて、class属性で付与するクラス名を切り替えています。また、データの送信には<amp-form>要素を使い、ボタンのタプに応じてXHR POSTリクエスト、その結果に応じてiineStateを書き換えています。

<!-- いいね!する -->
<form
  method="post"
  action-xhr="https://iine.ameba.jp/exec..."
  target="_top"
  class="hidden"
  [class]="!iineState.didExecute ? 'visible' : 'hidden'"
  on="submit-success:AMP.setState({iineState: {didExecute: true}})"
>
  <button type="submit">いいね!する</button>
</form>
<!-- いいね!をキャンセルする -->
<form
  method="post"
  action-xhr="https://iine.ameba.jp/cancel..."
  target="_top"
  class="hidden"
  [class]="iineState.didExecute ? 'visible' : 'hidden'"
  on="submit-success:AMP.setState({iineState: {didExecute false}})"
>
  <button type="submit">いいね!をキャンセルする</button>
</form>

Referrer Detection With <amp-dynamic-css-classes>

<amp-dynamic-css-classes>を使うとページが表示されている環境に応じて表示の切り替えができます。環境の値は動的にクラス名としてbody要素に付与されます。スタイルはCSS内に定義されるため、素早くレイアウトを反映できます。

現時点では、リファラやAMP Viewer内かどうか判定でき、アメブロではTwitterアプリ内での表示カスタマイズに利用しています。

<body class="amp-referrer-twitter-com"> <!-- Twitter時に付くクラス -->

スクリーンショット Referrer Detection With <amp-dynamic-css-classes>

Twitterアプリ上での表示のカスタマイズ。

Installing Service Worker With <amp-install-serviceworker>

<amp-install-serviceworker>を使って、一度検索結果やシェアからAMPページに訪れた際には、オリジナルページのService Workerをインストールします。AMPページからそのままオリジナルサイトに遷移する場合や、またあとでオリジナルサイトに遷移する場合にも、Service Workerがインストールされた状態で開始することができます(アクティベートされるかどうかはタブなどブラウザの状態に依存します)。

スクリーンショット Installing Service Worker With <amp-install-serviceworker>

Google検索結果で表示中のAMPページ。google.co.jpドメインで表示されているのにもかかわらず、ameblo.jpドメインのService Workerがインストールされている。

オリジナルサイトでは<amp-install-serviceworker>で、読み込む用のHTMLを用意し、その中でService Workerを登録する記述をします。そして、AMPページからdata-iframe-src属性でiframeとして読み込み、Service Workerを登録します。

<amp-install-serviceworker
  src="https://gamp.ameblo.jp/service-worker.js"
  data-iframe-src="https://ameblo.jp/_static/install-service-worker.html"
  layout="nodisplay"
/>

オリジナルサイトでのService Workerではアセットのプレキャッシュがされており、遷移時にはすでにそれらがキャッシュされた状態からページの表示が開始されます。そうすることで、表示・操作開始速度の改善や無駄なネットワークリクエストを削減することができます。


本記事では、AMPに用意されている様々な要素を使って、オリジナルサイトと同様のユーザー体験を構築する方法を紹介しました。

HTMLとCSSで宣言的に高品質のWebページを作れるところがAMPの良いところですが、さすがに<amp-bind>を使って状態管理をするとなるとプログラミング的な脳の使い方が必要そうです。

それでも他のライブラリやフレームワークのようにインストールの必要がなく、HTMLさえ記述できれば利用できます。サブ的な機能から徐々に利用してみるのも良いかもしれません。

アメブロでは今後も、AMPページでもオリジナルサイトのユーザー体験をできる限り再現できるように向上させていく予定です。