AMP ページでのカスタム JavaScript の使用
AMPは、調整や設定を行わずに使用できる高機能でシームレスなコンポーネントの使用を促進することで、ウェブ全体のすべてのユーザーに一貫して優れたエクスペリエンスを提供するよう努めています。
一部のウェブエクスペリエンスには、amp-bind
のバインド機能、 amp-list
の動的データ取得とテンプレート機能、および amp-mustache
をはるかに上回る大量のカスタマイズが必要です。そういった一回限りのケースのために、AMP は、ページの全体的なパフォーマンスを劣化させることなく任意の JavaScript を AMP ページで利用できるようにする <amp-script>
コンポーネントを作成しました。
カスタム JavaScript の挿入
AMP ページは、<amp-script>
コンポーネントを通じて カスタム JavaScript をサポートしています。以下の例は、amp-script
と URL から読み込んだ JavaScript ファイルの使用方法を示しています。
<!doctype html>
<html ⚡>
<head>
...
<script async custom-element="amp-script" src="https://cdn.ampproject.org/v0/amp-script-0.1.js"></script>
<body>
...
<amp-script layout="container" src="https://example.com/myfile.js">
<p>Initial content that can be modified from JavaScript</p>
</amp-script>
...
</body>
</html>
<amp-script>
コンポーネントは、メインページとは別のスレッドで実行する Web Worker を登録します。Web Worker には、Worker DOM による amp-script
の使用によって、自身の DOM のコピーを与えられます。これにより、Web Worker は、React や jQuery といった JavaScript ライブラリを変更せずに使用できるようになります。
amp-script
コンポーネントは、Web Worker スレッドとメインスレッド間でメッセージを送信し、メインの DOM で行われた変更を Web Worker の false DOM にエコーさせます。一方で、Web Worker が flase DOM を更新すると、その内容がメイン DOM に反映されます。
カスタムスクリプトのキャッシング
AMP キャッシュは、<amp-script>
で挿入されたカスタム JavaScript ファイルを、AMP コンポーネントスクリプトと同じ方法で配信することで、カスタム JavaScript によって 速度の低下が生じないようにしています。
AMP キャッシュは、JavaSciprt ファイルをプロキシして配信します。<amp-script>
を使用するページでは、それを含まないページと同じパフォーマンスエクスペリエンスがユーザーに提供されます。
<amp-script>
の使用
AMP ページが一貫して素早くスムーズな UI で読み込まれるように保証するために、<amp-script>
には制限があります。
初期化
Web Worker 内の JavaScript は、読み込み時に DOM に最小限の変更を許可しています。このフェーズで許可される変更は、以下のとおりです。
- イベントハンドラの登録。
- 複数の TextNode への TextNode の分割。それを必要とするフレームワークを可能にする目的があります。
<amp-script>
タグ内の DOM は、初期化の前後でほぼ同一である必要があります。
たとえば、以下のようなコードで起動するすとします。
<text> Hello world </text>
Worker DOM は、構造へのわずかな変更を許可しますが、コンテンツへの変更は許可しません。
<text>Hello </text><text>world</text>
DOM マニピュレーション
ユーザーエクスペリエンスとセキュリティの理由により、amp-script
は、DOM マニピュレーションに制限を設けています。
ユーザーインタラクション
ユーザーが、<amp-script>
コンポーネントにラップされた要素を操作する場合、カスタム JavaSciprt は必要なときに DOM マニピュレーションを素早く返す必要があります。デフォルトでは、DOM への変更は、最初のインタラクションから 1 秒未満許可されています。注目すべき例外は、コードが fetch
を介してネットワークからデータを取得する必要がある場合です。ここで、DOM の変更は、レスポンスがユーザーに返された後の 1 秒未満 の間要求できます。許可された期間外でスクリプトが DOM を変更すると、致命的なエラーが発生し、<amp-script>
コンポーネントによって Web Worker が終了されます。終了された <amp-script>
コンポーネントが再び実行することはありません。
プロンプトなしの変更
<amp-script>
コンポーネントの高さが固定である場合、DOM マニピュレーションに必要となるユーザーインタラクションはありません。
スクリプトのサイズ
AMP は、各ページのカスタム JavaSciprt を 150 キロバイトに制限しています。この制限は、そのページ上のすべての <amp-script>
コンポーネントで共有されます。すべての外部 JavaScript ライブラリは、個別の <amp-script>
コンポーネントにインポートする必要があります。
スコープ
カスタム JavaScript ファイルが対話するすべての DOM 要素は、<amp-script>
コンポーネントタグでラップする必要があります。これには、ほかの AMP コンポーネントも含まれます。<amp-script>
コンポーネントは、ドキュメントの <body>
要素ではなく、document.body
が <amp-script>
要素であるとみなします。
以下のドキュメントの <amp-script>
要素にインポートされたスクリプト内で document.body.appendChild(document.createElement('span'))
を呼び出すとします。
<body>
<p>Hello!</p>
<div>
<amp-script layout="container" src="customjs.js">
</amp-script>
</div>
</body>
これは、以下のような結果になります。
<body>
<p>Hello!</p>
<div>
<amp-script layout="container" src="customjs.js">
<span></span>
</amp-script>
</div>
</body>
イベントトリガ
すべてのイベントトリガを使用できます。
API の制限
一部の同期メソッドは、<amp-script>
で許可されておらず、Element.getBoundingClientRect()
といった別のものに置き換えられています。Web Worker に Element.getBoundingClientRect()
を実装することはできないため、その非同期の代替である getBoundingClientRectAsync()
が提供されています。getBoundingClientRectAsync()
は、直接結果を返す代わりに、Promise
を返します。
WorkerDOM 対応 API を確認するには、こちらのチャートを参照してください。
-
Written by @CrystalOnScript
with contributions from @fstanis