Yahoo!ショッピングで実践!表示速度カイゼン10選 〜 Web編 〜
こんにちは!
Yahoo!ショッピングのひらやま(@rhirayamaaan)です。
インターネットを使っていると、いろいろなページにアクセスしますよね。
ただ、ページにアクセスしたときに、なかなかページが表示されないと、めちゃくちゃイライラしませんか?
私はします!
実際に、ページ表示速度が速くなるだけで、そのサービスのカイゼンにつながるという話はよく聞く話です。
上記の記事にこんな言葉がありました。
スピード・イズ・ゴッド。スピード・イズ・ジャスティス。
家訓にしたいレベルの標語ですね。
これはもう本当におっしゃる通りです。
Yahoo!ショッピングでも実際に速度のカイゼンをすると、サービスのカイゼンにつながっています。
なので今回は、Yahoo!ショッピングが実施した 10 個ものカイゼン策を出し惜しみせず紹介しちゃいます!
1. 遅い API と直列な API を使う箇所を非同期化
APIの中には、レスポンスが遅い API や API を直列にたたかないといけないケースが出てきてしまうこともよくあると思います。
これをサーバーサイドで実行してしまうと、すべてのAPIの実行を待ってからページを表示することになるので、ページの表示が遅くなってしまいます…
しかし、ユーザーがページに訪れたときに最初に見るのは「ページ上部」のはずです。
なので、ページ下部にある情報の遅い API や直列な API の結果を非同期のタイミングで取得すれば、「ページ上部の表示」を速くできます!
ページ表示前の処理をギリギリまで抑えながら、ページ表示後にリッチな情報を取得するようにチューニングしています。
この対応で、なんと 200ms のカイゼンができました!
2. 間違った非同期の見直し
1 の作業をしていくと最初はどんどん早くなっていくので
もしかして…たくさん非同期にしていけば限りなく速くなるのでは!?
と思ってしまいがちです。
しかし世の中はそんなに甘いものでも単純なものでもないです。
甘い蜜には毒があるとはよく言ったものです。
Yahoo!ショッピングの検索結果ページでは、検索結果の画像を即時で読み込まずに、onload のタイミングで読み込むようにしていました。
画像はテキストに比べて容量が大きいなので、あえてページが表示された後に検索結果の画像を読み込んでいました。
しかし、すべての検索結果の画像を対象にしていたため、ページの上部に表示される検索結果の画像も後から読み込むようにしていました。
ページの上部にあるものは、即時で反映されなければいけないのはずなのに、闇雲に後から読み込むようにしてしまったのが原因です。
なので、検索結果の上部の画像だけは最初から表示されるようにしたことで、上部の表示速度が 1000ms もカイゼンできました!
見づらい画像で恐縮ですが、画像の表示タイミングが明らかに速くなりました!
3. 読み込み開始タイミングの調整
2では、画像の表示タイミングを、「ページが表示された後」から「ページが表示されるとき」に変更をしました。このカイゼンをしただけで表示速度が上がりました。
われわれはそれだけでは満足しきれず、さらに画像が表示されるタイミングを速くしたくなってしまいました!
ブラウザーが HTML を解釈する順序は、ご想像通り上から順番に解釈していきます。
そのため、画像の読み込む記述が上にあればあるほど読み込むタイミングが速くなります。
通常だと以下のように画像を読み込むと思います。
<html>
<head>
...
</head>
<body>
...
<img src="読み込みたい画像URL">
...
</body>
</html>
ブラウザーで実際に表示される部分は body タグの中のものが表示されます。
つまり、画像が読み込まれるタイミングは body タグを解釈し始めるまでは読み込まれません。
しかし、body タグより手前に head タグがありますね…?
なら、この場所に画像を読み込む記述をしてしまえばいいのでは!?
と思い、すぐに調べてみたところ「rel="preload" によるコンテンツの先読み」というドキュメントを見つけました。
https://developer.mozilla.org/ja/docs/Web/HTML/Preloading_content
まさしく head タグ内で preload する方法です! ドンピシャ!
これなら読み込んでおきたいものを早い段階で読み込めそうです!
さっそく書いてみます。
<html>
<head>
...
<link rel="preload" href="読み込みたい画像URL">
...
</head>
<body>
...
<img src="読み込みたい画像URL">
...
</body>
</html>
こうすれば、link タグがあるタイミングで画像を読み込ませることができます。
実際に検証してみると、読み込みタイミングも表示タイミングも速くなっていました!
これで、400ms のカイゼンができました!
4. 次ページで利用するコンテンツの準備
検索結果ページに訪れた方は、商品を検索している方なので、次に商品詳細ページへ訪れる人が多いです。
商品詳細ページへ訪れる人が多いということは、商品詳細ページの必要なものをあらかじめ検索結果ページで読み込んでおけば、次ページの商品詳細の表示速度を速くできます!
ただし、商品詳細ページのコンテンツは、当然検索結果ページでは必要のないものであることは間違いないため、ページを表示した後にゆっくりと非同期で読み込みます。
また、単純に検索結果ページで読み込んだだけでは余計に読み込むだけになってしまうので、しっかりとキャッシュの設定をしておくことが重要です。
この対応で、100ms のカイゼンができました!
前ページでの事前読み込み対応は、今の技術だと Service Worker を使うのが一番良いと思うのですが、対応ブラウザー問題があったり実装コストもかかったりするので、既存の技術で実現してみても効果があったのがとても良かったです!
5. JavaScript の読み込みを非同期化する
JavaScript を読み込むときは、以下のように記述すると思います。
<script src="JSのURL"></script>
こうしてしまうと、JavaScript のファイルの読み込みから処理が完了まで、ページの描画が全てストップしてしまいます。
そのため、よく取られる手法としては、ページの下部に script タグを配置するというのがあるかと思いますが、もっと良い方法がありました!
詳しくはこちらを見ていただければと思いますが、script タグには async という属性が存在します。
この属性をつけておけば、ページの描画をストップさせることなくファイルを読み込ませることができます。
対応はとても簡単で、下記のようにコードを書くだけです!
<script src="JavaScriptのURL" async></script>
この対応をしたことで、100ms のカイゼンができました!
しかし、これもまた闇雲に非同期処理にしていけばいいとは限りません。
例えば jQuery を使っている場合、jQuery の読み込みが完了していないと、自分で書いた JavaScirpt で「jQuery がないよー!」と怒られてしまう可能性があります。
JavaScript のファイルをたくさん読み込んでいる場合に、それらのファイルの依存関係をよく調査した上で実施した方が良さそうです!
6. 画像の画質調整
JPEGの画質の設定値は 0〜100 を設定できます。
値が小さければ小さいほど容量を軽くできますが、もちろん画質が落ちてしまって見るに堪えない画像になってしまいます。
以下の画像は、画質を 90 にしているときの画像です。
夏ですね!
それに対して、画質を 85 にした画像がこちらです。
これも夏ですね!
画質の設定値を 5 だけ小さくしましたが、画像の粗さの違いは全くわかりません。
それにもかかわらず、画質が 85 の方は 37KB も容量を軽くすることができました!
そしてこの対応で、50ms のカイゼンができました!
見た目が変わらないのに容量が少なくなっているのはとてもありがたいですね!
画質を少しだけ落とすことで、容量を軽くしてページ表示速度を上げるのはとても有効な手段です!
※ note の圧縮がかかっている可能性があるため、この記事内で表示されている画像は実際に検証したときの画像容量とは異なる場合があります。
7. デバイスに合わせた画像の表示
スマートフォンの画面解像度はどんどん良くなっていきます。
例えば iPhone であれば 300 ピクセルの画像を表示したい場合、最低でも 600 ピクセルの画像を用意して、画像がぼやけないようにする必要があります。
しかし、もちろん世の中にはそこまで解像度が高くない端末も多いです。
300 ピクセルの画像を表示したいなら、そのまま 300 ピクセルの画像を用意すればいい端末もあります。
そのような端末にも 600 ピクセルの画像を用意していたら、ただでさえ画像は重たいというのに、ただただ 2 倍に太らせて提供することになるわけです。
そんなときは、HTML の img タグに用意されている srcset 属性を使います。
<img src="画像URL">
普段であればこのように記述すると思います。
それを以下のように書き換えます。
<img src="画像URL" srcset="画像URL 1x, 1.5倍の画像URL 1.5x, 2倍の画像URL 2x">
上記のように書けば、ブラウザー側が表示するべき画像を自動で切り替えてくれます!
便利な世の中になりましたね…
※srcset が対応していないブラウザー向けに src は指定しておきます。
※詳しい書き方はこちらをご覧ください。
この対応で、20ms のカイゼンができました!
8. JavaScript と CSS の軽量化
サービスを運用していると、工数の兼ね合いでなかなかソースコードを見直す機会は減ってくると思います。
Yahoo!ショッピングでは AB テストを回しまくっているので、工数の兼ね合いでそのABテストのソースコードが残り続けてしまうケースも残念ながらあります。
しかし、ソースコードを見直せばサービス成長につながることは間違いないので、いらないソースコードをひたすら削っていきました。
それだけで、なんと 40KB も削減できました!
ただのテキストなのにこんなに削減できるとは… 少し反省しないといけないですね…
この対応で、20ms のカイゼンができました!
9. HTML の圧縮方式を変える
これはとてもシンプルなカイゼンです!
もともと HTML を、gzip で圧縮していました。
それを、ただただ、brotli に変えました。
この対応で、なんと HTML の容量が 13% も削減できました!
それによって、20ms のカイゼンができました!
10. 質と量の向上
ついに最後のカイゼンになりました。
最後はもう、圧縮よりも断然シンプルなカイゼンです。
それは…
「ハードウエアのリプレイス」と「スケールアウト」です!
最新スペックは単純に速いのです!
負荷が高いならスケールアウトすれば良いんです!
予算との兼ね合いはあると思います。
ただやはり、ここにお金をかけてでも速度を突き詰めたい!
それがサービス成長につながるのです!
この対応で、30ms のカイゼンができました!
最後に
このように、Yahoo!ショッピングはページ表示速度を追求し続けています。
しかし頑張って速くしても、サービスのカイゼンや機能追加も行っているため遅くなってしまうこともあります…
カイゼンしていく中で、機能追加によってページ表示速度遅くなりそうになっても、速度が落ちないように食い止めていくカイゼンも重要だと思っています。
最後の方のカイゼンは、大幅なカイゼンかと言われると思い悩むところもあると思います。
しかし、食い止めていくという視点からも、どんなに小さいカイゼンでもしていくべきだと考えています。
スピード・イズ・ゴッド。スピード・イズ・ジャスティス。
これを絶対に忘れず、これからもどんどん速くしていきます!
Yahoo!ショッピングのスタッフ全員が思っていることは、
みなさんのお買い物体験が少しでも良くなることです!
これを胸に、今日も速度をカイゼンしていきます。
これからもYahoo!ショッピングをよろしくお願いいたします!