RitoLabo

LaravelでAMPページを作成する実践編~AMPとは?基本を添えた実装入門~

  • 公開:
  • 更新:
  • カテゴリ: PHP Laravel
  • タグ: Laravel,Webpack,5.5,5.4,5.3,Build,sass,LaravelMix,AMP,CSS,LaravelElixir

前回のLarave&AMPに関する記事LaravelでAMPページを作成するベストプラクティスでは、LaravelにてAMP実装を行うためのロジックを紹介しました。

今回は、具体的な実装手順・方法について、AMPの基本も交えながら実際に手を動かしてソースコードと共に紹介したいと思います。

アジェンダ
  1. AMP
  2. AMP導入の是非
  3. AMP実装のゴール
  4. AMPの実装について
    1. 必須の記述がある
    2. 使えないHTMLタグと専用ampタグがある
    3. 外部CSSの読み込みは不可
    4. Web Fontsの使用について
    5. JavaScriptの使用にも制限がある
  5. LaravelでのAMP実装
    1. 開発環境について
    2. Laravel Mix(webpack)
    3. ルーティング
    4. コントローラ
    5. ビュー
    6. 動作確認

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 Project]コンポーネント / タグ

また、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%入ってくる事を担保した上で採用する必要があります。

[AMP Project]amp-img

外部CSSの読み込みは不可

AMPページでは、通常ページのように<link rel="stylesheet" href="~" />という形で外部CSSを読み込む事が出来ません。

<style amp-custom>~</style>
の形で、head内に出力する必要があり、しかもそれは50KB以内でなければいけません。

[AMP Project]スタイルとレイアウト

Web Fontsの使用について

LaravelではSASSなどにWebフォントの記述を行いコンパイルしている場合も多いですが、AMPページではhead内に専用タグで記述しなければなりません。

style amp-customの中で記述する事も可能ですが、50KBという制約がある以上、少しでも容量を節約したいところですから、AMP対応しているWebフォントについては専用タグで読み込み、対応していないWEBフォントをどうしても使いたい時だけにする方が良いでしょう。

[AMP Project]カスタム フォントの追加

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ファイルを一つにまとめる。という流れを取っています。

コンパイルの流れとしては、以下になります。

  1. laravel/resource/assets/sass 配下のSASSをコンパイル
  2. コンパイルされたCSSファイルをそれぞれ laravel/resource/assets/build ディレクトリへ出力
  3. 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.scss
webフォントの読み込みを行っている
reset.scss
リセット用CSS
page.scss
ページ用CSS
page-amp.scss
AMPのみで使うCSS

AMP専用の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">
<img src="{{asset('/images/amp-logo.jpg')}}" width="100%" >
</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",
"datePublished": "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ページ

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ページバリデーション表示

AMP validation successful.と表示されていたら、AMPとしての要件は全て満たされたと確認できます。逆に、エラーが表示されていたら修正が必要です。

ちなみに、エラーが表示されたまま公開するとどうなるのか?という事ですが、GoogleからAMPページであると認識してもらえないので、検索結果にはAMPページが表示されません。なので、バリデーションチェックには必ず100点で通過する事が求められます。

まとめ

作業は以上で完了となります。
AMP対応は素直にいく場合もあればそうでない場合も多く、まだまだ案件によって労力の振れ幅も大きいのですが、Laravelでの開発であれば上手く最少の工数・処理でAMP実装を行うロジックを構築できます。

AMP自体がまだまだ発展中とはいえ、既にGoogle検索結果には採用されており、先日、ついにYahoo!モバイル検索でもAMP対応が行われました。

これからもっとJavaScriptなども自由に記述できるようになる見通しも立ち始めてきたかなという中で、マーケティングを重視するプロジェクトであれば今すぐにでもAMP対応をという声が強いと思います。

そんな中で我々エンジニアが今の仕様でどこまでAMP実装を実現させ、プロジェクトのポジションを上昇させられるか。腕の見せ所かなと思います。

色々言いましたが、ロジック(設計)さえできてしまえばAMP自体の実装は全然難しくないので、是非試してみてください。

尚、今回の作業ソース一式はGithubからダウンロードできます。

[Github]www.ritolab.com-sample-sources-Laravel5.5-AMP