事の発端はGoogleが「2021年6月からCWV(コアウェブバイタル)の評価をランキングシステムに導入する」とアナウンスしたことでした。そのアナウンスにより「病院なび1」でもCWVの向上が優先課題として上がり、その解決策として、病院なびのAMP化を実施致しました。
この記事では、なぜAMPにしたのか、AMPへの切り替えを、どのように進めたのかを記します。
(病院なびではRailsを使っているので、RailsでのAMP化方法となります)
なぜAMP?
まず、AMP(Accelerated Mobile Pages)」とは、Googleが推進しているコンテンツを高速に表示されるための手法で、CWVに優れています。が、その分、制限も多いです。
AMP はウェブサイトを高速かつユーザーファーストにし、収益化するシンプルかつ堅牢なフォーマットです。AMP は一般的なプラットフォームへの配布を実現し、運用コストと開発コストを削減することで、貴社のウェブ戦略に長期的な成功をもたらします。
引用: https://amp.dev/ja/about/websites/
AMP Project には、デベロッパーが簡単に Core Web Vitals のしきい値を満たせるようにするという役割があります。
引用: https://developers-jp.googleblog.com/2021/02/core-web-vitals-amp.html
病院なびでのパフォーマンスのボトルネックはフロント側ということは分かっていました。フロント側をより詳細に調べて、CWVを少しづつ改善していていくという方法もありましたが、調査と改善を繰り返すのはどうしても時間がかかります。今回は2021年6月という期限があったのと、一部のページは既にAMPにしていたため、残りの主要ページをAMP化して、CWVの改善を目指すことにしました。
AMPの制限と課題
上述の通り、AMPではコンテンツを高速で表示するために、いくつかの制限を強いられます。一部列挙すると
- 基本、JavaScriptは利用不可。(amp-scriptでは使えるが、使える機能は限られている)
- CSSはinlineのみ。また、容量も75KBまで。
- imageタグなど特定のHTMLタグは AMP用のタグ(など)に置換する必要がある
また、AMPに切り替えるにしても、一挙にAMPにするのはリスクが高いので、既存ページ(以降、非AMPと記載)とAMPでのABテストを実施しながら、進めることとします。正しくABテストが出来るように非AMP/AMPのレイアウトは可能な限り同じにする必要があります。
つまり、非AMPの見た目のまま、中身はAMPで構成されているHTMLを作成する必要がありました。
そこで、非AMPとAMPを切り分ける仕組み作りから始めました
非AMP? それとも AMP?
まず第一歩として、非AMP/AMPの判定処理を作成する必要があります。病院なびでは、POROでAMP判定用Classを作成致しました。
class Amp
def initialize(user, page)
@user = user
@page = page
end
# AMP対象のユーザーか
def user?
# 基本はスマホユーザーの場合のみ true を返す
# それに加えてABテスト時では、cookie による判定も加えて非AMP/AMPの切り分け制御を実施
end
# AMP対象のページか
def page?
# 基本はAMP対象のページの場合 true を返す
# それに加えてABテスト時では、東京都のページのみなどの判定を加えて制御する
end
# AMPとして表示するか
def layout?
user? && page?
end
end
このAMPの判定条件を使って、非AMP/AMPの切り分けの仕組みを作っていきます。切り分けは大きく2パターンあり「テンプレートによる切り分け」 と「メソッドによる切り分け」です。
テンプレートによる切り分け
テンプレートによる切り分けは簡単で、Railsの機能を使います。
Railsで render 前に以下の実装を加えます
lookup_context.formats = %i[amp html] if amp.layout?
このようにすれば、AMPの場合のみ、Railsがテンプレートを探す際に、まずは*.amp.haml
を検索し、なければ、*.html.haml
を使うという動作が可能となります。
制限の箇所で記述した通り、AMPは外部からのCSSは読み込めないので、以下のようにテンプレートを分けます
非AMP
= stylesheet_link_tag(*stylesheets)
AMP
-#
実際の記述を説明部分だけ抽出して分かりやすく書き直してます
以下の実装は、定型だったり、 Sassのコンパイルを実施しているので、実際はキャッシュしています
:ruby
# AMP に必要な Boilerplate
# https://github.com/ampproject/amphtml/blob/master/spec/amp-boilerplate.md
style = <<~STYLE.lines.map(&:strip).join
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
%style{ "amp-boilerplate": "" }= style
%noscript
%style{ "amp-boilerplate": "" }
body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}
-# AMPではCSSはインラインでしか受け付けていないため、対象CSSを header に書き込む
%style{ "amp-custom": "" }
- stylesheets.each do |stylesheet|
-#
SCSS gemを利用して compress 化する
compress は BOM が付与されているので外す
!= SassC::Engine.new(stylesheet, style: :compressed).render.remove_bom
呼び出し元では非AMP/AMPは気にする必要がなく、以下のようになります
-# 非AMPの場合は _css.html.haml を AMPの場合は _css.amp.haml を読み込む
= render("layouts/css")
このように「テンプレートによる切り分け」は簡単なのですが、「メソッドによる切り分け
」はRailsを頼れないため、独自実装する必要があります。
メソッドによる切り分け
病院なびではalias_method_suffix
という独自メソッドがあります。ここでは詳しく記載しないですが、昔、Railsにもあった、alias_method_chain
( Railsの実装参考)と類似したメソッドです。実態は以下のようにsuffixed?
の結果により呼び出すメソッドを切り分けできます。
class Foo
class_attribute :suffixed, default: false
def foo
:foo
end
alias_method_suffix :foo, :bar, if: :suffixed?
def foo_with_bar
# _without_{suffix} で元のメソッドを呼べる
:"#{foo_without_bar}_with_bar"
end
end
Foo.new.foo #=> :foo
foo = Foo.new
foo.suffixed = true
foo.foo #=> :foo_with_bar
これを用いると、良い感じに非AMP/AMPのメソッドを切り分けできそうです。
(amp_layout?
はamp.layout?
のエイリアスメソッドです)
# amp_layout? が true の場合、*_with_amp メソッドを呼び出す
module AmpMethod
def amp_method(target_method)
alias_method_suffix(target_method, :amp, if: :amp_layout?)
end
end
はい、出来ました。では、これをどのように使うかをimage_tag
を用いて見ていきます。AMPの場合、通常の<img>
は使えず<amp-img>
を使う必要がありました。これを対応した実装は以下の通りです。
module ActionView
module Helpers
module AssetTagHelper
# amp_method が使えるようにモジュールを extend する
extend AmpMethod
amp_method :image_tag # amp の場合は image_tag_with_amp を呼ぶ
# AMP用
def image_tag_with_amp(source, options = {})
# 幅・高さが未指定の場合は scriptエラーとなるため、未指定でも可能な layout='fill' とする
options[:layout] = :fill unless options[:width] || options[:height]
# 空タグ <amp-img /> ではなく、内容の無いタグ <amp-img></amp-img> にするために tag.public_send している
# https://amp.dev/ja/documentation/components/amp-img/
tag.public_send('amp-img', nil, options.merge(src: source))
end
end
end
end
こちらも「テンプレートによる切り分け」と同様に呼び出し元では非AMP/AMPを意識する必要はありません。
# 非AMPの場合は <img> で、AMPの場合は <amp-img> で作成される
= image_tag("amp.jpg", alt: "amp")
そしてAMP化へ
仕組みができたので、もう勝ったも同然です。AMPのページでは自動で、CSSはインラインとなり、画像はamp-img
で作成されます。呼び出し側は何も変える必要はありません。
AMP化したいページを amp.layout? => true
として、ページ表示してみましょう。URLに #development=1
を付与すると、ページロードと共にAMPのvalidation
が実行されます。ブラウザのコンソールにAMP validation successful.
と出れば、AMP化成功となります。
ただ、現状ではエラーが出まくると思います。JSを使っていたり、他の制限に引っかかっていることもあります。ただ、仕組みは出来上がっているので、あとは粛々と対応していくだけです。
まずは、エラーとなっている箇所を判定し、それを「テンプレートによる切り分け」or「メソッドによる切り分け」で切り分けます。この時、AMP側は何も返さないとします。(テンプレートであれば、空ファイル、メソッドであればnil
を返す)
これを繰り返し、AMP validation successful.
となる状態に持っていきます。
その後に先ほど切り出したAMP側を今度は一つづつ対応していきます。対応ごとにAMP validation successful.
となることを確認して下さい。全て対応すれば、晴れてAMP化達成となります。
この進め方の良い所は一度切り分けを作ってしまえば、あとは自動で切り替わるところです。
病院なびでは、「詳細ページ」と「口コミページ」という構成は似ているが、表示している内容が異なるページがあります。まずは詳細ページをAMP化したのですが、その後の口コミページのAMP化は amp.layout?
をtrue
にすれば、AMP化することができました。
AMP化してみて
当初の目的としてのCWVの改善は叶ったかと思います。6月に主要ページはAMPにして、Google Search Console上での不良もしくは要改善のページは、ほぼ良好なページとなりました。2021年12月では96.5%が良好となっています。
ただ、やはり制限が厳しいのは事実です。現状でも「その仕様はAMPでは実現が難しい」という場面があります。特にJSが使えないので、Google以外のツール(アナリティクスなど)を使っている人は、それがAMPに対応しているかの確認が必要です。ちなみにGoogleAnalytics4
は2021年12月時点でも、AMP非対応です。
参考: https://github.com/ampproject/amphtml/issues/24621
また、開発が活発に行われているReactとかと比較すると、開発スピードがどうしても気になります。私は東京で開催されたAMPカンファレンス AMP Conf 2019
に参加したのですが、その時からAMPの新機能はそれほど増えていないように思います。そのカンファレンスで Bento AMP を鋭利作成中と発表しておりました。これはAMP以外でもAMP のコンポーネントが使えるというもので、最近、正式公開になったと知りました。カンファレンスから2年で、大きな機能リリースは Bento AMP のみと思います。
参考: https://www.suzukikenichi.com/blog/google-officially-launches-bento-amp/
病院なびでは当初のCWV改善という目的は達成できましたが、AMPの制限が足枷になっているのも事実です。また、CWVに関しても、まだ、改善の余地はありますので、他のツールを検討しながらも、よりよりサービスを目指していければと思います。
-
「病院なび」は、全国23万件以上の医療機関・薬局・薬店を検索できる医療機関検索サイトです。https://byoinnavi.jp ↩