Yomerumoのスマホページの表示速度が約半分にまでなった話

主にYomerumoのフロントエンドを担当しておりますGMOアドマーケティングのy.aです。

Yomerumoは芸能・エンタメ中心に、有名人の情報やニュースなど様々な情報を豊富に取りそろえているニュースサイトです。
http://news.merumo.ne.jp/

Yomerumoのスマホページ

アクセスの多くはモバイル端末からなのですが、以前からそのモバイルで表示速度の遅さに頭を抱えていました。

下記は遅かった頃(高速化施策を始める前)の表示速度です。

期間 Android iOS
[2017/01/01~2017/03/31] 平均読み込み時間(秒) 14.88 6.16

※Google Analytics調べ

遅いですよね。。Google Analyticsの「平均読み込み時間」はページビューの開始(ページへのリンクがクリックされたときなど)から、ブラウザで読み込みが完了するまでの時間を表すため、一概に「ページが表示されるまでの時間」とイコールとは限りません。ただそれでもiOSはまだともかく、Androidはここまで遅いとユーザーに不便を与えていることは明らかです。
これまでもCSS/JSファイルの圧縮やCSSスプライトなどの対応はしていたのですが、今年に入ってから「表示が遅い」「より根本的に見直ししてほしい」というご意見を内外から少しずつ頂くようになりました。

ということで3月から本格対応を始めたのですが、3カ月間の試行錯誤を経て出た表示速度の結果が以下の表です。

期間 Android iOS
[2017/01/01~2017/03/31] 平均読み込み時間(秒) 14.88 6.16
[2017/07/01~2017/08/31] 平均読み込み時間(秒) 7.77 3.23

※Google Analytics調べ

表示速度をそれまでの約1/2に改善出来ました!当然数値だけではなく、体感的にも早く感じられるようになっております。

今回はそのYomerumoで行った表示速度UPのための施策について皆さんに共有します。
なお以下で紹介する施策は全てフロントエンドの内容となりますのでご了承ください。


指標と計測ツール

対応するにしても、何がサイト表示を遅くしているか原因が分からないといけません。
そしてその原因を探るためには計測が必要です。

当初速度改善はPageSpeed Insightsを参考に進めていました。
https://developers.google.com/speed/pagespeed/insights/?hl=ja

ただご存知の通り「PageSpeed Insights」はベストプラクティスの達成具合を見ています。つまりサイトの表示を遅くしている原因を提示してくれるわけではありません。(もちろんベストプラクティスを達成していないことが遅い原因と言えなくもありませんが。)
そういう意味では提示内容を全てクリアしても必ずしも表示速度向上を達成できるとは言い切れないところがあります。
また今回は複数メンバーで週ごとに検証していたため、体感的な表示速度を定量的に、かつページ表示までの流れを詳細に、そして可能であれば問題点の共有が容易なものが必要となっておりました。

そこで新たな速さの指標として「Speed Index」、そしてそれを計測するために主に「WebPagetest」を使用することにしました。
https://www.webpagetest.org/

「Speed Index」は可視範囲内での表示速度の平均値を測る指標です。つまりファーストビューでの表示速度を表しています。「Speed Index」はスコアが低ければ低いほど描画が早いということになります。今回の目標としている体感的な表示速度改善の指標として一番合っていました。

黄色でハイライトした箇所が「WebPagetest」で計測したSpeed Indexの値
よく参考にしていたのは「Speed Index」を算出するVisual Progress

また何をもって早いと考えるのかという中で、よりユーザー視点でページの表示過程を確認できる「WebPagetest」のビデオキャプチャ(filmtrip)がとても役に立ちました。

filmstripの一例。3Gで計測したというのもありますが、他社メディア様と比べても遅いです。。

ChromeのDevツールでも「Performance」を使えばビデオキャプチャを確認できますが、「WebPagetest」であれば先の「Speed Index」のスコアとあわせてビデオキャプチャも撮れます。
記録可能なビデオキャプチャであるためチーム内で問題事項の共有が容易になりました。


遅くしていた原因

上記「WebPagetest」等で計測し分析を進めた結果、Webフォントアイコンの使用、JSの動的なコンテンツ表示処理などフロント側の実装の問題点がたくさん見つかりました。
ただその中でも一番表示速度に影響を与えていたのは「ファーストビュー時のリクエスト数の多さ」でした。

例えば当時のトップページでは、ファーストビューのタイミングでYomerumoからリクエストする外部JSの数は14~5、画像は20程度でしたが、広告から出される広告に関するリクエスト(js、imgなど)は約250以上近くありました。。

3月時点でのリクエスト数。この中のjsとimageの多くが広告表示に伴ってリクエストされたもの

結局その他諸々全て合計すると毎回ファーストビューの時点でリクエスト数は平均で340~400余に上りました。これでは遅いのも当たり前で、特に古い端末であればあるほどその影響が大きかったと考えています。

上記リクエスト数の結果、当然ロードするバイト数も大きい

今までも全く気付いていなかったわけではないのですが、先の「WebPagetest」で他のニュースサイト様の分析をしても、Yomerumoのリクエスト数の多さが表示速度に影響を与えているのは明らかでした。


どう対処したか

大きくいうと以下の2つです。
1. ファーストビューコンテンツの表示調整
2. リクエスト削減

1. ファーストビューコンテンツの表示調整

ファーストビュー内にあるコンテンツ、パーツなどの表示を早くする調整です。以下2つの対応をしました。

1-1. localStorageの活用

通信を伴って動的に生成する要素がある場合、Ajaxを使用していることがほとんどだと思いますが、非同期ですと最初の描画のタイミングに間に合わず、後でぽつっとそのコンテンツが出てくることがあると思います。YomerumoでもAjaxで取得しているところがあったのですが、それまではプレースホルダーも置くことなく空白のままでした。

そこでそう頻繁に更新のない箇所については、毎回Ajaxせず初回で一度jsonを取得後localStorageを使用してローカル内に保存し、アクセスから10分間は通信せずlocalStorageから出すように変更しました。

これ自体は表示速度の数値には大きく影響与えませんでしたが、通信をしない分早くコンテンツが表示されるようになりました。

1-2. ファーストビュー内にあるパーツの表示調整

一部のパーツで「Font-Awesome」(Webフォントアイコン)を使用していたのですが、これもロードされるまではその部分が空白となってました。
少しでも早く感じてもらうには、やはりなるべくネットワークに依存しない形にするのが一番です。
Yomerumoで言うと右上にメニューのためのハンバーガーアイコンがありますが、当初「Font-Awesome」で表示していたものを、CSSでの3本線(border+border-radius)に変えました。

またロゴ画像も以前はCSSスプライトで表示していましたが、サイズが大きくなりすぎたため、表示が遅れがちでした。
ファーストビュー内の画像は少しでも早く表示させるために先のCSSスプライトから切り離し、タグで出力することによって可能な限り最初の描画に間に合わせるようにしました。

ビデオキャプチャで見ると分かりますが、それまでと比べてコンテンツ・パーツの表示が早くなりました。以下Before/Afterの画像です。

Before. ヘッダのコンテンツがほとんど表示されていない
After. 最初の描画の時点でほぼ表示されている

2. リクエスト削減

2-1. JSをバンドル

読み込む外部JSの数が一般のサイトと比べても多すぎました(14~15)。
これはサイト内に古くから存在していたものは触れず、とりあえず追加に追加を重ねていったため、結果こんな数になってしまったのです。。(反省)

要らないものは除去、まとめられるものはまとめるのが一番です。
このタイミングで改めて各JSの役割・依存関係を徹底調査し明確にした上で、不要なものを削除、残ったものはまとめる(バンドル)ために「webpack」を使用しました。

結論から言うとJSファイル全てを「webpack」によるバンドル対象とすることはできませんでしたが、14~5ほどあった細かいJSファイルを3~4に減らすことに成功しました。

蛇足ですが、「webpack」はリクエスト削減より、バンドルすることでJS同士の依存関係をはっきり出来るのが大きな利点だと思います。確かにリクエスト削減目的で導入しましたが、結果「require」で組んでいくことでコードの見通しが大変良くなり、今後の保守を考えた上でも対応して良かったと感じています。
通常、既に運用が始まっているサイトでwebpackを導入するのは色々とハードルも多いかと思うのですが、これにより不測のトラブルを回避できると思います。

2-2. ファーストビューにないコンテンツはインビュー読み込みに変える

今回の施策の中で一番効果が大きかったのはこれです。
画像・広告についてはファーストビューで表示されるもの以外は全てインビュー読み込みに変更しました。

やり方の詳細は省かせていただきますが、ある要素がインビューになったらそれをトリガーとして、画像のリクエスト、広告のリクエストを起こすイベントの形に変えたのです。
おかげで今までファーストビュー時に約400あったリクエストが、修正後わずか約100リクエストほどになりました!

赤がBefore、青がAfter
当然バイト数も減ってます

かなり大きいですよね。体感的にも一番ビフォーアフターで違いがはっきりと感じられました。


まとめ

以上の対応の結果、それまでの表示速度の半分の速さを実現することができました。

それまで目標としていた某他社ニュースサイト様と比べても今は「Speed Index」、そして「Load」時間においても、画像も広告数もはるかに多いYomerumoの方が早くなりました。(それまでは全く手の届かないくらい差があったので達成したときは大変感慨深かかったです。。)
実施の過程で、途中いくつも失敗があったのですが無事表示速度改善ができたと言えると思います。
ただ進めれば進めるほど表示速度に関してはまだまだ改善余地あることが分かりました。もっと言えばもう少しUXを考慮に入れた丁寧な調整ができると感じております。

例えばユーザーが「何をもって早く感じるか」ということの分析もその一つです。
取組み通じて分かったのがファーストビューの「最初の描写」(Render Start)が少し遅くなろうとも「最初の描画時点で構成するコンテンツがほぼ揃っている」方が、「最初の描画が早くともコンテンツがあまり揃っていないもの」よりも体感的には早く感じることでした。

例えばページが2.0秒のタイミングでRender Startされようとも、その時にコンテンツが60%近くしか出ていないのであれば、3.0秒のタイミングで、コンテンツが80%出ている方がユーザー体験としても良いでしょう。

上がAfterで下がBefore。確かにBeforeの方が描画が早く始まっているが、コンテンツが40%台から描画されているためユーザー的に早いサイトと感じるかは微妙なところ

フロントエンドで言うと「非同期」というキーワードが表示高速化と結ばれがちですが、
先の気づきもあって今回対応していったなかで多少描画が遅くなろうとも、Render Start時点で可能な限りVisual Progressの比率が高くなるようにあえて「同期」的にした箇所もあります。
よりユーザーに快適に使ってもらうことを考えた場合、「非同期」にすることが100%「正」にならない可能性もあるかもしれません。

赤がbefore、青がAfter。Afterの方がRender Startは遅いものの赤より早く100%に達している

ただいずれにしても、高速化という点だけにおいてはファーストビューにないコンテンツは可能な限り後読みの形にすることは大事なはずです。
モバイル時代に最適化したAMPHTMLを見ても分かりますが「<amp-img>」「<amp-ad>」などこれらのカスタムエレメンツもビューポート等に応じて読み込みを遅らせたりしています。
インビュー読み込みにすることで、無駄なリクエストが発生しないためユーザーにとって望ましい形であることは間違いないです。
(ちなみにYomerumoはAMPにも対応しております!詳しくはこちら

今回紹介した施策以外でもサイト内で一部Resource HintsのAPIを使用していたり、その他まだまだ説明しきれなかった細かいことがたくさんやったのですが、現時点で効果のほどが定かでなかったためまた別の機会にお伝え出来ればと思います。
さらに今後SSL化対応すればService Workerの活用も視野に入りますので、対応した際にはまた共有出来ればと思います。

読者の方にとっては当たり前のことばかりだったかもしれませんが、表示高速化進めるにあたって「PageSpeed Insightsしか使用していなかった」あるいは「なかなか指針を決められない」場合などに、この記事が少しでも有用なものになりましたら幸いです。

今後もYomerumoはよりユーザーに快適にサイトを利用してもらうよう進めてまいります。

※本記事で提示した「WebPagetest」の計測値は全て「From: Japan – EC2 – Chrome – Emulated iPhone 6 – 3GFast – Mobile」の条件で出したものです。