あの人気サービスは、Webサイトを高速化するために何をしているか

最近、Webサイトの高速化が話題になっています。

Wantedlyでもサーバーサイドのレスポンス速度はしっかりトラッキングして取り組んでいましたが、フロントエンドはまだまだやれることがあると認識し、悔しさを胸にさっそく動き出しています。

取り組むに当たって、まずは事例を集めていくことから始めました。サーバーサイドの実装を見ることはできないですが、フロントエンドは頑張れば覗けるので、Webサイトの高速化に取り組んでいそうな他のサービスをじっくり観察することで、自分たちのプロダクトに最適な方法を選択できるはずです。

様々な種類のサービスを提供しているサイトを調査してみると、その高速化の手法はサービスごとに結構違っていて、学ぶことが想像以上に多かったので、ブログにまとめてました。同じようにWeb高速化へのモチベーションが高まっている皆さんの参考になれば幸いです。

Netflix

まずは、動画ストリーミングサービスのNetflixです。

アクセスしてすぐに気付くのは、かなりの数の画像が並んでいることです。魅力的な作品が並んでいるように見せるためには、この画像はとても重要でしょう。これらの画像は、WebP形式で配信されていました。WebP形式は、Googleが開発した高い圧縮率を誇る画像フォーマットです。対応しているブラウザは限られているので、非対応ブラウザではJPEGが代わりに表示されるようになっていました。

それらの画像を含むほとんどのリクエストはHTTP/1.1でした。このような大量のリクエストは、HTTP/2に対応することで並列にリクエストが行えるためによりはやくダウンロードできると一般的に言われていますが、対応していないのは既存のインフラ的な問題か、そのほかに理由があるのでしょう。

ページは、ReactによるSSR(サーバーサイドレンダリング)が行われているため、ページ内容を表示するためにJSの実行を待つ必要がないようになっています。CSSは一つにまとめられていました。HTTP/2に対応している場合、細かく複数に分けたほうが、並列ダウンロードやキャッシュが効きやすくなるため良いと言われていますが、HTTP/1.1なので一つにbundleしてしまった方が速いのでしょう。

JSはいくつかに分割されていて、レンダリングをブロックしないようにdefer属性付きで読まれていました。が、その中でも一番大きな動画プレイヤーに関するJS(約500KB)は、async属性付きで読まれていました。動画の再生ボタンがクリックされるまでは必要ないため、急いで読み込む必要はないものはasyncで読むのは鉄則でしょう。

調べているときに、JSファイルのURLがすごく特徴的で気になりました。URLにtruenoneなどの文字列が含まれているのです。

https://codex.nflxext.com/%5E2.0.0/truthBundle/webui/0.0.1-shakti-js-12ea5c82/js/js/bootstrap.js,common%7Cbootstrap.js/2/gj03ggfV01fW0k0l0j4gf_gi060z02gd4yg1fY0igegc4sf-fQg31R/bk/true/none

これは、どうやらJSのbundleサイズを小さくするための努力の証拠みたいです。Netflixをよく使う人は分かると思いますが、UIを大きく変えるA/Bテストを頻繁に行なっています。このような場合に、あるユーザーにとって必要がないmoduleまで含めてbundleしてしまうと、ファイルサイズが大きくなってしまい、結果的に遅くなってしまいます。ビルド時に静的解析を行って、条件ごとに最適化されたJSを複数パターン作って配信するようにしているみたいです。

UIはレスポンシブになっているように見えますが、モバイルでアクセスした場合はReactなどを必要としないシンプルなページを表示していました。動画は、アプリで再生する前提の設計なので、重たいJSを入れる必要はないでしょう。

参考リンク:

Airbnb

JS(特にReact)を書いている人は、Airbnbのブログなどをよく参考にしていると思うので、アーキテクチャについてよく知っている人も多いと思います。

Hypernova(Airbnbが開発)を使ったSSRにより、JSを待たずに高速に表示できるようになっています。その際、CSSはインライン化されているため、外部のCSSは一切ロードせずに表示まで行うことができます。

JSはwebpackでビルドされており、Routingに基づくCode Splittingをしています。ページにアクセスしたタイミングでは最低限のJSだけ読むようにすることで、ユーザーに対してインタラクティブになるまでの時間を短くすることができます。そのあと、必要になったタイミングで必要なJSをロードするようになっていますが、それだけではなく、ユーザーのインタラクションより前にロードしておくようようなスケジューリングの仕組みも入っているようです

アクセス時のリクエストのHTTPヘッダにLinkヘッダが含まれており、そこでCSSやJS, それにフォントを先読みするようなpreloadの設定が入っていました。これにより、HTMLをパースして必要になってからダウンロードを開始するのではなく、すぐにダウンロードを開始すべきリソースを指定できるので、表示やインタラクティブになるまでの高速化に繋がります。特にフォントはCSSの中から参照されることが多いので、CSSをダウンロードして評価して必要になったタイミングでしかダウンロードされないため、文字の表示が遅れたり、代替フォントが表示されたりする、FOUT(Flash of unstyled text)という問題も起きやすいので、preloadはとても効果的です

またAirbnbのウェブサイトで特徴的だと思ったのは、JSでRoutingしているにも関わらず、宿泊先の個別ページへのリンクをクリックした時は、強制的に別タブで開くようになっていることです。単純に表示する速度だけなら、Ajaxでデータだけ受け取りクライアントで描画した方が確実に速いはずです。しかし、複数の宿泊先候補から比較して選択するというユーザーの行動にとって、別タブで開くことの方がユーザーにとって利益が大きいという判断をしているのだと伺えます

参考リンク:

Twitter

Twitterといえば、モバイル版がPWA(Progressive Web App)化したことで大きく話題になりました。Webサイトだけどアプリのような使い勝手を実現しており、Web好きの人はかなり盛り上がったと思います。

NetflixやAirbnbと違ってSSRは行われておらず、まずアクセスするとアプリと同じスプラッシュ画面が表示されます。その間に初期データやJSをロードして、タイムラインの描画を進めます。また基本的なデータはアクセス時のレスポンスに含まれているため、必要なAPIコールも少なくなるように工夫されています
Reactが使われており、Routingに基づいてCode Splittingされているので、必要なJSだけを最小限でロードしてタイムラインを表示し、必要になったタイミングで残りのJSをロードするようになっています。

さらにService Workerが使える環境では、インストールされるとバックグラウンドで残りのJSをダウンロードしにいきます。なので、必要になったタイミングではすでにキャッシュされているため、より高速にロードを完了することができます。これらのアセットは、ユーザーがアクセスしていない時にも定期的に最新のものを再取得するようになっているはずなので、ほとんどの場合JSのダウンロードは必要ないでしょう。ルートのレスポンスもキャッシュされているため、オフラインでもすぐにスプラッシュ画面が表示され、キャッシュされたデータでタイムラインまで表示することができます。

また、Resource Hintsを積極的に使っていました。preconnectやdns-prefetchなどのmetaタグを指定することで、リクエストを送る前に、事前にドメインの名前解決やコネクションを貼っておくことで、高速化が効果があると言われています。どの程度効果があるのかは検証していないので分からないですが、設定して損することはないものだと思います。

TwitterのメインのUXは、タイムラインをどんどんスクロールしていくことだと思うので、そこに対するチューニングにもかなり力を入れています。また、ユーザーのインタラクションに素早く応えて画面を切り替えるためのチューニングも注目です。当たり前のことですが、アクセス時に素早く表示するだけでなく、使用中にユーザーの操作に素早く応えることはとても重要です。

参考リンク:

Alibaba

中国最大のECであるAlibabaも、PWA化されたモバイルウェブサイトを提供しています。

ページ遷移は、JSでハンドリングせずに、ブラウザの通常のページ遷移が行われています。しかし、ほとんどのページ遷移はService Workerにより事前にキャッシュされているために、一瞬で表示が完了します。あるページを表示した後に、そこからリンクされているURLをService Workerに教えることで、バックグランドでダウンロードしておくことで、実際に遷移した際にそのキャッシュを使って高速に表示できます。

Service Workerが動いているため、オフラインでも表示できます。例えば、この前見ていた商品を再度検討したいと思った時にオフラインでも表示することができために、事業として効果が期待できるのかもしれないです(もちろんオンラインにならないと買えないと思うが)。オフライン対応するかどうかは、提供するサービスにしっかり考えるべき点だと思います

JSは、メインのJSが一つ読まれて、その中から必要なJSを随時非同期で読み込むような形になっています。こういうパターンだと、Airbnbのようにpreloadを指定してしておくと、ブラウザは事前にそのJSの必要性を知ることができるので、速くダウンロードできるはずです。ただ、どのリソースが必要になるのかをサーバー側が知っている必要があるので、簡単には対応できるものではないので、きちんと仕組みを整える必要がありそうです。

参考リンク:

Medium

メディアプラットフォームであるMediumにとって、SNSや検索エンジンから流入してきたユーザーに即時に記事を表示することはとても重要でしょう。

CSSのインライン化や、JSのasync読み込みなど、基本的な部分はしっかり抑えられています。基本的にページ遷移はJSでハンドリングされていて、他のページに遷移するときは必要なデータだけAjaxで取得して、クラアント側で描画しています。

サーバー側とクライアント側のそれぞれで記事の描画を行なっているので、ReactのようなJSフレームワークなどを使ってSSRしているのかと思いきや、そのようなフレームワークは使われてないようでした。では、どうやってSSRしているのかと注意深く見てみると、どうもサーバー側とクライアント側で別の方法で描画しているようです。初期表示では、サーバーから返されたHTMLを表示しておき、そのあとでJSがロードされたタイミングでクライアントで生成したHTMLと、まるっと入れ替えしていました。これは想像ですが、SSRの技術が普及してくる前から独自の方法で高速化に取り組んだ結果、このような構成になっているのではないでしょうか。

記事ページに大きく表示される画像は、Mediumの特徴の一つでしょう。大きな画像をダウンロードするのは時間がかかるため、ここにも大きな工夫があります。まず、すごく小さい画像をダウンロードして、それをCanvasを使ってぼかした画像を表示します。大きな画像がダウンロードできたら、アニメーションで切り替えることで、空の領域やダウンロード途中の画像する代わりに、特徴的な演出でユーザーの気を引くこともできている素晴らしい手法だと思います。<noscript> 要素を使うことでJSがない場合は、そのままの画像を返すようにしているところも抜け目がないです。

以上、動画サービスからSNS、ECやメディアなど、5つの種類のサービスの高速化の取り組みを覗いてみました。共通している手法から、サービスごとに特徴的な手法も多く見られて、プロダクトの特徴や、既存の資産によって、最適解は変わってくるんだろうなと実感しました(もちろん、これらのサービスもさらに改善されていくと思いますが)。闇雲に他のサービスを真似するんじゃなくて、自社のプロダクトをよく理解してから取り組む必要があるので、とても難しいけど本気で取り組む価値がある問題だと思います

Wantedlyも高速化に向けて動いています。ぜひ興味がある人は、お気軽に話を聞きに来てください!

Wantedly, Inc.'s job postings
Anonymous
Picture?1522984899
13669820 936237219818622 6326533494602722630 n
14657533 972092329604336 1798550457830116463 n
Bab9cecf cf88 4b39 abde a86e1216fe68?1522752584
29314535 958271110997121 1225513861119540143 n
103 Likes
Anonymous
Picture?1522984899
13669820 936237219818622 6326533494602722630 n
14657533 972092329604336 1798550457830116463 n
Bab9cecf cf88 4b39 abde a86e1216fe68?1522752584
29314535 958271110997121 1225513861119540143 n
103 Likes

Weekly ranking

Show other rankings

Page top icon