AMP Optimizer単体での挙動を詳しく調べてみた

シェアする:

この記事をおすすめしたい人

  • AMP Optimizerに興味がある人
  • AMPのSSRをやってみたい人
  • AMPコンポーネントのスクリプト追加がめんどくさい人
  • つまりオレ

今回はAMPをサーバサイドレンダリングしてくれるAMP Optimizerの挙動について詳しく調べてきました。

AMP Optimizerがやっていることを正確に知ることで、自分がコードを書く際の簡略化、併用するツールの選択などができるようになると思います。

準備として最小構成のAMP HTMLを用意

AMP Optimizer単体で動作させれる環境がNuxt.jsしかないので、Nuxt.jsでギリギリValidなAMPを書きます。

Nuxt.jsでAMP Optimizerを動かす実験の時にアプリテンプレートというのを使えばNuxt.jsでもすっからかんのテンプレートが作れそうだったので、これをフル活用します。

具体的にはNuxt.jsインストールフォルダに「app.html」というファイルを作ります。

内容はこれから説明します。

まずは最小構成のHTML5

デフォルトのテンプレートはこんな↓感じ。


<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
  <head {{ HEAD_ATTRS }}>
    {{ HEAD }}
  </head>
  <body {{ BODY_ATTRS }}>
    {{ APP }}
  </body>
</html>

このままではNuxt.js側からめりめりコードを足されてしまうのでバッサリいきます。


<!DOCTYPE html>
<html>
<head>
</head>
<body>
{{ APP }}
</body>
</html>

<body>内だけは後で実験しやすいよう「{{ APP }}」を残し、他は全部バッサリです。

次に最小構成のAMP

先ほどのテンプレートの<html>に「amp」とだけ足して、AMP Validatorが出したエラーを順次潰して、エラーがなくなったタイミングを最小構成とします。

完成品がコレ↓。


<!DOCTYPE html>
<html amp>
<head>
<link rel="canonical" href="https://XXXXXXXXX/">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<style amp-boilerplate>
(略)
</style>
<script async src="https://cdn.ampproject.org/v0.js"></script>
</head>
<body>
{{ APP }}
</body>
</html>

<style amp-boilerplate>の中身を省略していますが、これはAMP必須のコードです。 AMPのロードが終わるまで画面を非表示にするようです。 ないとエラーが出ます。 詳細はAMP公式サイトを見てください。

あとは、Nuxt.jsがデフォルトで読み込むJavaScriptのエラーが出ていたので、nuxt.config.jsに以下のコードを追加します。


export default {
  render: {
    injectScripts: false,
  },
}

これもAMP Optimizerの実験で得た知識です。 アホな実験を繰り返していますが、少しだけ自分の成長を実感します…。 renderプロパティの詳細についてはNuxt.js公式サイトを見てください。

最小AMPをAMP Optimizerに処理させた結果

さっきのシンプルAMPをAMP Optimizerに通します。

導入についてはAMP Optimizer導入の記事を見てください。 今回は自分でValidなAMPを用意しているのでAMPモジュールは導入せず、Optimizer単体です。

OptimizeされたHTMLがコレ↓。


<!doctype html>
<html amp i-amphtml-layout i-amphtml-no-boilerplate transformed="self;v=1">
<head>
<meta charset="utf-8">
<style amp-runtime i-amphtml-version="012006180239003">(略)</style>
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<link rel="preload" href="https://cdn.ampproject.org/v0.js" as="script">
<script async src="https://cdn.ampproject.org/v0.js"></script>
<link rel="canonical" href="https://XXXXXXXXX/">
</head>
<body>
(略)
</body>
</html>

サーバサイドレンダリングによる変化

AMP Optimizerを通すとサーバサイドレンダリング(以下SSR)されます。
(オフにはできないっぽいです)

SSRについてもOptimizer導入の記事で話しましたが、簡単に言うと「AMPランタイムがタグを書き換える処理をサーバ側でやってしまう」機能です。

それを示すコードが<html>に追加されています。

他にもランタイムが読み込むスタイル<style amp-runtime>が事前にロードされ、ランタイムのロードが終わっているので<style amp-boilerplate>が削除されています。

その他の変化

必須ランタイムのpreloadが追加されています。 読み込みがちょっと速くなるやつです。
(既に同じタグがあった場合は重複して追加はありません)

あと、このソースは見やすいよう改行を追加していますが、出力されたHTMLでは無駄な改行や空白は削除されていました。

他には頭の「DOCTYPE」が小文字にされていたり、canonicalが<head>内末尾にずらされたりしています。

つまり、ValidなAMPをAMP Optimizerに委ねた場合、実質SSRのみ(+微調整)です。

位置ずらしが起きるコードを探す

canonicalの場所が変わっていたので、他に位置ずらしが起きているコードを探してみます。 調査したところ、かなりの書き換えが起きているようでした。

以下、表示優先度の高い順にまとめていきます。

charset

以下のコードです。


<meta charset="utf-8">

どこに置いても、<head>直下に置かれます。 自前で置かなかった場合にも<head>直下に挿入されます。

style amp-runtime

以下のコードです。


<style amp-runtime i-amphtml-version="012006180239003">(略)</style>

charset直下に挿入されます。

meta領域

<style amp-runtime>の後に、<head>内の全てのmetaタグが再配置されます。

並び順は、必須のviewportを含め、元の配置順です。

viewportがなかった場合は、meta領域末尾に追加されます。

また、以下のコードはmetaタグではありませんがviewportとセットになっているようで、必ずviewport直下に置かれます。


<link rel="preload" href="https://cdn.ampproject.org/v0.js" as="script">

おそらくですが、metaタグのうちviewportだけは必ずチェックをかけるもので、この後にくるscript領域より前に書いておく必要があるからセットになっているんでしょう。

script領域

meta領域の後に、AMPコンポーネントのscriptがまとめて再配置されます。

元の配置位置によらず、v0.jsは必ず一番上に配置されます。 なかった場合にも一番上に追加されます。


<script async src="https://cdn.ampproject.org/v0.js"></script>

その後は、AMP Optimizerが決めている順番で配置されるようです。 規則性が分かりませんでした。

未使用コンポーネントを読み込んでいた場合にも削除はされません。

その他の領域

<title>タグ、<link>タグ、構造化データなどの残ったタグの配置領域です。

「rel=”icon”」のみ領域内最上位へ配置換えが起きます。

残りのタグは元の並び順で配置されます。

<head>内の再配置まとめ

まとめると、<head>内の再配置の優先度は次の順番です。

  1. charset
  2. style amp-runtime
  3. metaタグ
  4. scriptタグ
  5. 残ったタグ

なるべく速く表示できるようにとの配慮なんですかね。

AMP Optimizerの機能一覧

AMP Optimizer公式サイトに書かれてある機能を追加で検証していきます。

サーバサイドレンダリング

これはもう確認しましたね。

ランタイムによる書き換えをサーバサイドで行い、ブラウザには処理済みのHTMLを渡して高速化する機能です。

個人的に一番注目していた機能でした。

AMP OptimizerはデフォルトでSSRされるようになっていて、オプションでオフにすることはできません。

AMPコンポーネントスクリプトの自動読み込み

コレ!

僕的にはSSRと並んでAMP Optimizerの目玉です!

<body>内でAMPコンポーネントを使うと、自動で必須スクリプトを追加してくれるんです!!

例えば、SNS投稿の埋め込みがしたいと思って、<body>内に以下のようなコードを追加します。


<amp-facebook></amp-facebook>
<amp-instagram></amp-instagram>
<amp-twitter></amp-twitter>

すると<head>内にはv0.jsの直下に必須スクリプトの読み込みが自動で追加されます。


<script async src="https://cdn.ampproject.org/v0.js"></script>

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

順番は…配置順でもないしアルファベット順でもないし、よく分かりません。

SSRされるのでHTMLのコードも以下のように追記されます。


<amp-facebook class="i-amphtml-layout-container" i-amphtml-layout="container"></amp-facebook>
<amp-instagram class="i-amphtml-layout-container" i-amphtml-layout="container"></amp-instagram>
<amp-twitter class="i-amphtml-layout-container" i-amphtml-layout="container"></amp-twitter>

いや、これはねー、ページ毎に必要なスクリプト管理するの、ほんとめんどくさかったんでかなりの神機能です。

この機能はAMP Optimizerのオプション「autoExtensionImport」をfalseにすると無効化できます。


export default {
  hooks: {
    render: {
      route: async (url, result, context) => {
        const AmpOptimizer = require('@ampproject/toolbox-optimizer');
        const ampOptimizer = AmpOptimizer.create({
          autoExtensionImport: false,
        });
        
        result.html = await ampOptimizer.transformHtml(result.html)
      }
    },
  },
}

が、当然InvalidなAMPになりますし、コンポーネントもちゃんと動きません。

<head>内に自分でスクリプト読み込みを書いていたとしても、AMP Optimizerが重複して追加したりすることはないので、オフにする理由はないでしょう。

必須タグの自動追加

AMPには<head>内に書いておかないとエラーが出るタグがいくつかあります。


<meta charset="utf-8">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<script async src="https://cdn.ampproject.org/v0.js"></script>
<link rel="canonical" href="https://XXXXXXXXX/">

非SSRの場合はこれら4つに加えて<style amp-boilerplate>が必須です。

AMP Optimizerでは<head>内にこの4つが見つからなかった場合、自動で追加してくれます。 <html>に「amp」属性がなかった場合にも自動追加。

canonicalだけは自動で内容を判断できないのでこの↓ように形だけ追加されます。


<link data-auto rel="canonical" href=".">

なので、使っているフレームワークやCMS側で先に書いてやる方がいいでしょう。

AMP Optimizerに処理させる場合はこう↓です。


export default {
  hooks: {
    render: {
      route: async (url, result, context) => {
        const AmpOptimizer = require('@ampproject/toolbox-optimizer')
        const ampOptimizer = AmpOptimizer.create()
        
        result.html = await ampOptimizer.transformHtml(result.html, { canonical: 'https://XXXXXXXXX/' })
      }
    },
  },
}

ちなみに、この機能もオプションで無効化できます。


export default {
  hooks: {
    render: {
      route: async (url, result, context) => {
        const AmpOptimizer = require('@ampproject/toolbox-optimizer');
        const ampOptimizer = AmpOptimizer.create({
          autoAddMandatoryTags: false,
        });
        
        result.html = await ampOptimizer.transformHtml(result.html)
      }
    },
  },
}

この機能も重複出力したりはしないので、オフにする必要性があるのかは分かりません。

ヒーロー画像のプリロード

「amp-img、amp-iframe、amp-video、amp-video-iframeのヒーロー画像を自動判別してlink rel=preloadを追加」とあったので、amp-iframeにplaceholderを指定すると…


<amp-iframe>
<amp-img placeholder src="https://XXXXXXXXX/test.jpg"></amp-img>
</amp-iframe>

確かにプリロードが追加されています。


<link rel="preload" href="https://XXXXXXXXX/test.jpg" as="image" data-hero>

「data-hero」って書いてるしこれのことでいいのかな?

ボイラープレートの削除

今回実験に使ったテンプレートはボイラープレート入りでしたが、確かに削除されてましたね。

不要なスペース改行の削除

AMPを高速化させる手段として、無駄なスペースや改行を削除し、HTMLを軽量化してくれます。

開発中はソースを確認するケースがあると思うので、改行削除されると見にくいなーという場合はオプションでオフにできます。


export default {
  hooks: {
    render: {
      route: async (url, result, context) => {
        const AmpOptimizer = require('@ampproject/toolbox-optimizer');
        const ampOptimizer = AmpOptimizer.create({
          minify: false,
        });
        
        result.html = await ampOptimizer.transformHtml(result.html)
      }
    },
  },
}

CSSのキーフレームをページ末尾に移動

CSSのアニメーション用の「@keyframes」をCSS欄から削除して、<body>末尾へ移動してくれるそうです。

やってみるとこう↓なりました。


<style amp-keyframes>
@keyframes XXXXXXXXX { (略)}
</style>
</body>
</html>

これは…CSS軽量化のためなんでしょうか…?
それか、<head>内で読み込むCSSサイズを減らして表示を速くするとか…。

ただし、ベンダープレフィックスをつけて「@-webkit-keyframes」としていた場合には移動は行われないようです。
(それが必要かは別にして)

AMPフレームワークとフォント読み込みの最適化

これは…v0.jsのpreloadが自動追加されることでいいのかな…。


<link rel="preload" href="https://cdn.ampproject.org/v0.js" as="script">

同様にWebフォントを追加すると、「dns-prefetch」が追加されていました。


<link rel="dns-prefetch preconnect" href="https://fonts.gstatic.com" crossorigin>

amp-scriptのCSP生成

CSPというのはContent-Security-Policyの略だそうです。

amp-script書いたことないのでさっぱり意味が分かりませんが、amp-scriptのページにあったサンプルスクリプトを追加すると、<head>に次の2行が追加されました。


<script async src="https://cdn.ampproject.org/v0/amp-script-0.1.js" custom-element="amp-script"></script>
<meta name="amp-script-src" content="sha384-wqO1iCAaG9o-IsyJEQ4db1fumWaMmAAYa0nbbYvojtvQ3BPbr3EZLeOZFY_iIA30">

上は必須スクリプトの読み込みなので、おそらく下の行がCSPなんでしょう…。

AMP Optimizerがやってくれないこと

WordPressのAMPプラグインはやってくれるのに、AMP Optimizerがやってくれないことがいくつかあります。

<style>タグの変更

AMPでは<style>は「<style amp-custom>」と書く必要がありますが、AMP Optimizerはこの書き換えをやってくれません。

<style>タグの統合

AMPでは<head>内に複数の<style>タグを置くことが許されておらず、ひとつの<style amp-custom>にまとめる必要がありますが、AMP Optimizerにこの機能はないようです。

<img>タグの変更

AMPではHTML5にあるタグの名称が変わっているものがあります。

<img> → <amp-img>、<iframe> → <amp-iframe>などです。

AMP Optimizerは<img>を見つけても<amp-img>に置き換えてくれたりはしません。

iframeも同様で、さらにamp-iframeには必須スクリプトもありますが、読み込み自動追加も機能していませんでした。

<form>も似た境遇ですが、こちらはタグ名が変わらないせいか必須スクリプトの自動追加が確認できました。 ただし、書き方が少し違うので機能はしていなさそうです。

カスタムJavaScriptの削除

AMPでは自前のJavaScriptが使えません。

が、カスタムJavaScriptが読み込まれていた場合にも、Validにするために削除したり、amp-scriptに置き換えたりする機能もAMP Optimizerにはないようです。

なぜAMP Optimizerはやってくれないのか

WordPressのAMPプラグインとはコンセプトが違うからだと思います。

AMPプラグインは「HTML5をAMP化する」プログラムで、AMP Optimizerは「AMPを最適化したAMPにする」プログラムなんだと思います。

AMP OptimizerもHTML5を渡すとほぼほぼValidなAMPになって返ってきますが、それ以上は面倒見てられんわ!ってことなんでしょう。

まとめ

AMP OptimizerはAMPを最適化してくれる素晴らしいツールでした!

高速化、管理コストの軽減、公表していない最適化(<head>内の並べ替え)などとてもありがたい。

が、その用途はあくまで「AMPから最適化したAMPへの変換」を想定しているようで、AMPを知らない人へのサポートはそんなに手厚くありません。

HTML5からのAMP化を考えている人は、AMPを勉強するか、他のツールを併用するかしましょう。

以上、WordPressからお届けしました!

シェアする: