LaravelでAMPページを作成する実践編~AMPとは?基本を添えた実装入門~
- 公開日
- 更新日
- カテゴリ:Laravel
- タグ:Laravel,Webpack,Build,sass,LaravelMix,AMP,CSS,LaravelElixir

前回の Larave& AMP に関する記事Laravel で AMP ページを作成するベストプラクティス では、Laravel にて AMP 実装を行うためのロジックを紹介しました。
今回は、具体的な実装手順・方法について、AMP の基本も交えながら実際に手を動かしてソースコードと共に紹介したいと思います。
Contents
AMP
AMP(Accelerated Mobile Pages) とは、ユーザエクスペリエンス向上を目的として Google が推進している、モバイルページを高速に表示させるための手法によって作成されているページの事を言います。 Google と Twitter がオープンソースプロジェクトとして立ち上げました。
検索結果からの話をすれば、AMP ページとして実装されたページを Google がキャッシュし表示する事で、高速レベルを超え瞬時にページを表示させる事が出来ます。
AMP 導入の是非
メリット、デメリットは以下のようになります。
AMP ページを導入する事のメリット
- 表示が高速になるので、「離脱率が下がる」「コンバージョンが上がる」事が期待出来る。
- Google CDN が保持したキャッシュが表示されるので、AMP へ流入すればするほど、その分自サーバの負担が減る。
- モバイル検索結果の AMP カルーセルに表示されたらうれしい。
- 時代の流れにしっかり乗れた事の満足感が得られる。
AMP ページを導入する事のデメリット
- AMP のルールに沿った実装が必要
デメリットはほとんどなく、オウンドメディアの発展を考えるならば AMP 対応しないという選択肢はほぼ皆無でしょう。故に、AMP 対応の是非については、「[do]やる・やらない」というよりは「[can]できる・できない」で決まってくるように思います。 AMP 対応実装の際に場合によっては細かい調整が必要になるので、我々エンジニアの専売特許、腕の見せ所でもあるかなと思います。(ちなみに実装が難しいという意味ではなく、一時期、AMP 対応に後ろ向きな発言をするブロガーの記事が相当数出回っていたので、WordPress にプラグイン突っ込んでるだけの人では完全に AMP 対応させるのはまだ難しいよねっていうお話)
AMP 実装のゴール
AMP 対応を行う事についての戦略的なゴールは先ほど話した部分である程度固まると思いますが、では開発側としては、何を以て AMP 実装のゴールとするべきなのでしょうか。
「通常ページと同一の機能、見た目で AMP 対応を完了させる」
現段階ではおそらくこの一点かなと私は感じています。
0からオウンドメディアやシステム開発を行う場合に AMP 対応も行うというのであればさほど難しい事はありません。そこも含め設計を行うので、無駄のない開発が行えるでしょう。
しかし実際の開発現場では、既に開発を終え運用を行っているシステムに対して AMP 対応を行うという、後発での開発案件もまだまだ多いです。
2017 年時点、AMP自体がまだ新しいだけに、システムが大きければ大きいほど、「通常ページと同一に」という単純な事が案外素直に実現できない事も多かったりします。
それだけに、言葉は簡単でも開発側として AMP 実装のゴールと掲げるには現時点では十分に大切な要素であると私個人としては感じています。
AMP の実装について
AMP を実装していくにあたり、最も重視しなければいけないのが、AMP-HTML ページを作成する際のルールです。
これについては公式ドキュメント を確認するのが一番ですが、ざっくり以下の通りになります。
必須の記述がある
通常ページでは不要ですが、AMP ページでは必ず入れないといけない META タグなどがあります。
使えない HTML タグと専用 amp タグがある
認められていない HTML タグがあり、併せて AMP-HTML 用のタグというのも存在します。これらを切り替える必要があります。
例えば、画像を表示する場合は<img src="~">は使えず、<amp-img src="~"></amp-img>タグとして置き換える必要があります。
また、amp タグには、必須の属性も漏らさずに出力する必要があります。例えば画像の場合は、必ず width と height を入れなくてはいけません。 <amp-img src="~" width=XX height=XX></amp-img>
ここで注意したいのが、出力した属性の値が空の場合はエラーとなります。例えば
<amp-img src="~" alt="" width=XX height=XX></amp-img>
のように、必須属性がきちんと入っていても、alt 属性が空っぽの場合はこれもまたエラーとなります。
Laravel 含め、PHP フレームワークを使った WEB アプリケーション開発の際は、こういった属性を動的に出力する場合が多いと思うので、必須属性ではないものを出力する場合には、値が 100%入ってくる事を担保した上で採用する必要があります。
外部 CSS の読み込みは不可
AMP ページでは、通常ページのように<link rel="stylesheet" href="~" />という形で外部 CSS を読み込む事が出来ません。
<style amp-custom>~</style>
の形で、head 内に出力する必要があり、しかもそれは 50KB 以内でなければいけません。
Web Fonts の使用について
Laravel では SASS などに Web フォントの記述を行いコンパイルしている場合も多いですが、AMP ページでは head 内に専用タグで記述しなければなりません。
style amp-custom の中で記述する事も可能ですが、50KB という制約がある以上、少しでも容量を節約したいところですから、AMP 対応している Web フォントについては専用タグで読み込み、対応していない WEB フォントをどうしても使いたい時だけにする方が良いでしょう。
JavaScript の使用にも制限がある
AMP ページでは、通常ページと同じように自由に JavaScript(以下、JS)を書く事が許可されていません。
まず、インラインスクリプトは許可されていません。<script>~</script>として直書き禁止という事です。
基本的には、サポートされているスクリプト(carousel や lightbox)に関して<script async custom-element="~" src="~">として宣言を行い、専用の amp タグで記述を行う(といってもスクリプトを書けるわけではない)という流れになっています。
一応、2017 年 12 月現在の情報では、ある程度自由に JS を書けるようになる方向では進められているそうです。(どういう形で書けるのかはまだわかりません)
ただし、Youtube や Twitter, Facebook など SNS の埋め込み系に関しては専用タグが用意されているので困りません。
[AMP Project]サードパーティ コンテンツを追加する
ここまでざっくりとした紹介ではありますが、AMP 実装が面倒だと感じるかもしれません。ですが制限もある中で JS部分を除けば大抵の事は出来るようになっているので、まずは実際に手を動かして AMP ページを作成してみるとまた理解も深まると思います。
Laravel での AMP 実装
それではここから実際に Laravel で AMP ページを実装していきます。
開発環境について
今回の開発環境については以下の通りです。
- Linux CentOS 7
- Apache 2.4
- PHP 7.1
- Laravel 5.5
作業に入る前に、Laravel& AMP のロジック思想については以下に基づいて進めていきます。
Laravel で AMP ページを作成するベストプラクティス
そして前提として、Larave Mix(webpack)を使ってのコンパイルが行える状態で進めますので、必要な場合は以下を参考にセッティングを行ってください。(Laravel 5.3 以下の場合は Laravel Elixir(Gulp)になります)
Laravel Mix(Webpack) を導入し sass や JavaScript をビルド、minify する
また、laravel フレームワークのルートディレクトリを laravel/ とします。
Laravel Mix(webpack)
style の記述については SASS で行い、Laravel Mix(webpack) でコンパイルを行います。
その際に、一度に全てをまとめるのではなく、一度、SASS ファイル単位で CSS へコンパイルを行い、ビルドされた CSS ファイルを一つにまとめる。という流れを取っています。
コンパイルの流れとしては、以下になります。
- laravel/resource/assets/sass 配下の SASS をコンパイル
- コンパイルされた CSS ファイルをそれぞれ laravel/resource/assets/build ディレクトリへ出力
- laravel/resource/assets/build 配下の CSS ファイルを1つにまとめ、laravel/public/css 配下へ出力する
上記の流れでビルドを行うために、webpack.mix.js へは以下の様に記述しています。
- laravel/webpack.mix.js
-
mix.sass('resources/assets/sass/app.scss', '../resources/assets/build/css/') .sass('resources/assets/sass/reset.scss', '../resources/assets/build/css/') .sass('resources/assets/sass/page.scss', '../resources/assets/build/css/') .sass('resources/assets/sass/page-amp.scss', '../resources/assets/build/css/') .styles( [ 'resources/assets/build/css/app.css', 'resources/assets/build/css/reset.css', 'resources/assets/build/css/page.css' ], 'public/css/app.css' );
こうする事で、AMP ページの際に必要な style のみを拾える(=不要な style は取らない)という仕組みを構築できます。
やってみるとわかりますが、50KB という容量制限は結構シビアです。長く運用するメディアであれば今後 style が増えていく事も考えられるので、実装時点で容量にはある程度の余裕を持っておく事が望ましいです。
また、通常ページの style に bootstrap を使っている場合は、コアソースだけで 100KB を超えるため AMP ページでは使えません。
なので、その場合はここで bootstrap に代わる AMP 用の CSS を作っておきます。上記でいうと page-amp.scss になります。
style を1つにまとめて public ディレクトリに出力する=通常ページ用の CSS ファイルになるので、まとめる際には page-amp.css は対象となっていない事が上記ソースから確認できると思います。
ちなみに、上記ソースに記載されているファイルの説明は以下の通りです。
app.scssweb フォントの読み込みを行っている reset.scss リセット用 CSSpage.scss ページ用 CSSpage-amp.scssAMP のみで使う CSSAMP 専用の CSS については、bootstrap の代替という使い方でなくても、AMP ページのみで適用したい style があった時などにもこういった形で使えます。
コンパイルについて
SASS を CSS へコンパイルする場合に npm コマンドを叩きますが、AMP ページ実装時はできるだけ production でビルドするようにしましょう。
Laravel Mix で SASS をコンパイルする場合、npm コマンドは2つあります。
# laravel のルートディレクトリへ移動
cd /path/to/laravel
↓
# A. ビルドを行う
npm run dev
# B. ビルド・ minify を行う
npm run production
前者、A の方はコンパイルのみを行うので、SASS ファイルをそのまま CSS ファイルへビルドしますが、後者、B の方はさらに minify(圧縮)を行います。
minify を行う事で改行や余計な空白などを除去してくれるので、最少サイズで CSS ファイルを生成できます。よって、ソースコードの容量を節約できます。
何度も言いますが、50KB という容量制限とはシビアに向き合うマインドで臨むと開発もスムーズにいきます。
ルーティング
ここからは MVC部分です。ルーティングを記述していきます。今回は通常ページと AMP ページの2つの記述を行います。
- laravel/routes/web.php
-
// 通常ページ Route::get('sample/page', 'PageController@index'); // AMP ページ Route::get('sample/page.amp', 'PageController@index');
http://YOURDOMAIN/sample/page
http://YOURDOMAIN/sample/page.amp
それぞれのアクセスに対して、Page コントローラの index アクションを実行するという流れになっています。
というのも、通常ページだろうが AMP ページであろうが、この後のコントローラでモデルから取得するデータは同一であるべき(AMP 用の追加を除いては)なので、どちらのアクセスに対しても、同じコントローラ&アクションを実行するのが望ましいです。
「同一であるべき」の根拠について、以下の Google ウェブマスター向け公式ブログの記事をチェックしてください。
2017 年 11 月 28 日 Google ウェブマスター向け公式ブログ
コントローラ
次に、コントローラを作成します。
laravel ルートディレクトリに移動し、以下の artisan コマンドを叩いて PageController.php を生成します。
# laravel のルートディレクトリへ移動
cd /path/to/laravel
# artisan コマンドでコントローラを生成
php artisan make:controller PageController
# 実行結果
[demo@localhost laravel]# php artisan make:controller PageController
Controller created successfully.
生成された PageController を開き、以下を記述していきます。
- laravel/app/Http/Controllers/PageController.php
-
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class PageController extends Controller { public function index(Request $request) { // AMPページの判定 $parse_url = $this->parseUrl($request->url()); $is_amp = $this->isAmp($parse_url); // canonical設定用URL $canonical_url = $this->getCanonicalUrl($request->url()); if($is_amp) { // styleデータを取得 $styles = $this->getStyleCustom(); $message = 'AMPページ'; return view('amp.page', ['message' => $message, 'canonical_url' => $canonical_url, 'styles' => $styles]); } else { $message = '通常ページ'; return view('page', ['message' => $message, 'canonical_url' => $canonical_url]); } } /** * URLをパースする * @param $request */ private function parseUrl($url) { return parse_url($url); } /** * URLからAMPページ指定か判定する * @param $parse_url * @return bool */ private function isAmp($parse_url) { if(strpos($parse_url['path'],'.amp') !== false){ return true; } else { return false; } } /** * Canonical用のURLを返却する * @param $url * @return mixed|string */ private function getCanonicalUrl($url) { if(strpos($url,'.amp') !== false){ return str_replace('.amp', '', $url); } else { return sprintf('%s.amp', $url); } } /** * CSSファイルからAMPに必要なものを返却する * @return string */ private function getStyleCustom() { $data = ''; $data .= file_get_contents(resource_path('assets/build/css/reset.css')); $data .= file_get_contents(resource_path('assets/build/css/page.css')); $data .= file_get_contents(resource_path('assets/build/css/page-amp.css')); $target = array('@charset "UTF-8";'); $change = array(''); $data = str_replace($target,$change,$data); return$data; } }
メインの index() アクションですが、まずはアクセスされた URL から AMP ページであるか否かの判定を行い結果を $is_amp 変数へ格納し、同じく URL から canonical設定用 URL を取得しています。
index() アクション以下のメソッドはよくある PHP関数を切り出したものですが、一点だけ、getStyleCustom() メソッドについて解説します。
ここでは、resource ディレクトリの中にコンパイルした CSS ファイルから、AMP ページに必要なものだけを取得し、一つの style コードにまとめています。
今回は4つの SASS ファイルをビルドしており、そのうちの3つだけを AMP 用の style として採用しています。
採用しなかった1つの CSS ファイルは、フォントを読み込むものなので、AMP ページでは不要としています。(web fonts は amp タグで埋め込む)
ビュー
ビューを作成します。今回は通常ページと AMP ページで分けて作成します。
通常ページと AMP ページを分けて作るかどうかは議論が分かれるところですが、今回はデモンストレーションなのでわかりやすさを重視して分ける事とします。
通常ページ用と AMP ページ用のビューソースは以下になります。
- laravel/resources/views/page.blade.php
-
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <link rel="amphtml" href="{{$canonical_url}}"> <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <title>title</title> <link rel="stylesheet" href="{{asset('/css/app.css')}}" /> </head> <body> <div class="container"> <p> <h1 class="page_title">AMP Sample Page</h1> <p class="image_wrap"> <div class="img_wrap"><img src="{{asset('/images/amp-logo.jpg')}}" width="100%" ></div> </p> <p> AMP実装サンプルページです。 </p> <p class="message"> このページは <span class="message_em">{{$message}}</span> です。 </p> </section> </div> </body> </html>
- laravel/resources/views/amp/page.blade.php
-
<!DOCTYPE html> <html amp lang="ja"> <head> <meta charset="UTF-8"> <title>AMP Page</title> <link rel="canonical" href="{{$canonical_url}}"> <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <script type="application/ld+json"> { "@context": "http://schema.org", "@type": "NewsArticle", "headline": "Open-source framework for publishing content", "date": "2015-10-07T12:02:41Z", "image": [ "logo.jpg" ] } </script> <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript> <style amp-custom> {{$styles}} </style> <script async src="https://cdn.ampproject.org/v0.js"></script> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Raleway:300,400,600"> </head> <body> <div class="container"> <section> <h1 class="page_title">AMP Sample Page</h1> <p class="image_wrap"> <amp-img src="{{asset('/images/amp-logo.jpg')}}" alt=logo width=1200 height=630 layout=responsive></amp-img> </p> <p> AMP実装サンプルページです。 </p> <p class="message"> このページは <span class="message_em">{{$message}}</span> です。 </p> </section> </div> </body> </html>
上記ページそれぞれの差分や AMP ページの特徴など、以下にソースコードと一緒にまとめました。
// 通常ページ
<html lang="ja">
↓
// AMP ページ
<html amp lang="ja">
// 通常ページでは AMP ページの URL を指定
<link rel="amphtml" href="{{$canonical_url}}">
// AMP ページでは通常ページの URL を指定
<link rel="canonical" href="{{$canonical_url}}">
// AMP ページでは以下タグは必須
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
<script async src="https://cdn.ampproject.org/v0.js"></script>
// 構造化データ(json)はここに展開
<script type="application/ld+json">~</script>
// AMP ページの style はここに展開する
<style amp-custom>
{{$styles}}
</style>
// AMP ページではこの形で Web フォントの指定
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Raleway:300,400,600">
// AMP ページでの画像タグの設置
<amp-img src="{{asset('/images/amp-logo.jpg')}}" alt=logo width=1200 height=630 layout=responsive></amp-img>
動作確認
実装が完了したので、実際にブラウザからアクセスして動作確認を行います。
ルーティングで設定した通り、
http://YOURDOMAIN/sample/page
で通常ページを、
http://YOURDOMAIN/sample/page.
で AMP ページが表示されます。
まずは通常ページから
このページがベースとなり、同じ見た目の AMP を作る事が AMP 実装のゴールとなります。
それでは AMP ページ
CSS の入れ替えや HTML タグの変更を行いましたが、通常ページと同じ見た目を再現できました。
ちなみに、ブラウザのデベロッパーツールを開いてコンソール画面を表示させ(Google Chrome なら F12 キー押下→ Console)「Powered by AMP - HTML - Version 」と表示されていたら、AMP ページとして認識されているという事が確認できます。
この時点でコンソール画面にエラーが表示されている場合は修正が必要です。
AMP ページのバリデーション
きちんと AMP ページとしての要件をクリアしているかどうかを調べる方法があります。
URL に「#development=1 」とパラメータとつける事で、AMP ページのバリデーションを行う事が出来ます。 URL としては
http://YOURDOMAIN/sample/page.amp#development=1
になります。この URL でアクセスしコンソール画面を確認してみます。
AMP validation successful.と表示されていたら、AMP としての要件は全て満たされたと確認できます。逆に、エラーが表示されていたら修正が必要です。
ちなみに、エラーが表示されたまま公開するとどうなるのか?という事ですが、Google から AMP ページであると認識してもらえないので、検索結果には AMP ページが表示されません。なので、バリデーションチェックには必ず 100 点で通過する事が求められます。
まとめ
作業は以上で完了となります。
AMP 対応は素直にいく場合もあればそうでない場合も多く、まだまだ案件によって労力の振れ幅も大きいのですが、Laravel での開発であれば上手く最少の工数・処理で AMP 実装を行うロジックを構築できます。
AMP自体がまだまだ発展中とはいえ、既に Google 検索結果には採用されており、先日、ついに Yahoo!モバイル検索でも AMP 対応が行われました。
これからもっと JavaScript なども自由に記述できるようになる見通しも立ち始めてきたかなという中で、マーケティングを重視するプロジェクトであれば今すぐにでも AMP 対応をという声が強いと思います。
そんな中で我々エンジニアが今の仕様でどこまで AMP 実装を実現させ、プロジェクトのポジションを上昇させられるか。腕の見せ所かなと思います。
色々言いましたが、ロジック(設計)さえできてしまえば AMP自体の実装は全然難しくないので、是非試してみてください。
尚、今回の作業ソース一式は Github からダウンロードできます。