こんにちは、編集長の白石です。
今回は、HTML5 Experts.jpでWebパフォーマンスに関する特集を行うにあたって、エキスパートの皆様による誌上座談会を開催してみました。
通常であれば数時間語っても尽きないような話を、1時間強でみっちり聞いてきました。 Webパフォーマンスの改善について、初心者から上級者まで楽しめる、有用な記事になっているかと思いますのでどうぞお楽しみください。
エキスパート紹介
白石 皆様、本日はお集まりいただいてありがとうございました!まずは簡単に自己紹介をお願いできますでしょうか?
竹洞 株式会社SpellDataのCEOを務めている、竹洞です。Webパフォーマンスには10年間くらい関わっており、年間200サイトくらいの計測に携わっています。 今度から、Instart Logicのエヴァンジェリストも務めることになりました。Instart Logicは、Application Delivery Networkを標榜する、いわばCDNの第三世代とも言えるものです。
白石 Contents DeliveryじゃなくてApplication Deliveryなんですね。その用語は、Instart Logicが独自で使っている用語なのか、それとも広く使われようとしている用語なんでしょうか?
竹洞 業界では広まりつつある用語ですね。具体的には仮想化技術と機械学習と組み合わせて、アプリケーションのリソース配信をこれまでにないレベルで最適化しようとするものです。
白石 なるほど、コンテンツデリバリーの業界は、そんなふうな進化が起こっているんですね…全然知りませんでした。次は川田さん、お願いします。
川田 川田です。ピクシブ株式会社の執行役員で、Chief Culture Officer (CCO:最高文化責任者) を務めています。 ここ最近は少しエンジニアリングから離れていますが、以前はWebパフォーマンス関連の記事を執筆したり、W3Cのメーリングリストで発言したりしていました。
最近だと、さくらインターネットさんと組んでImageFluxという画像変換のクラウドサービスを提供開始しました。弊社のサービスは、画像についてはユーザー様の要求レベルが極めて高いのです。
そこで得たノウハウを、モバイル時代のサービスの課題、それこそ、低速なネットワーク、多様なスクリーンサイズといった、画像の変換ニーズにあわせる形で他のサービス提供者のみなさんにも使ってもらえるようにしてます。
矢倉 ピクセルグリッドという会社でフロントエンドエンジニアを務めている矢倉です。Webの標準化にも関心がありまして、いろいろと関わっています。 パフォーマンスについては、レンダリングやスクロールと言った、クライアントサイドでの最適化に関心があります。
Webパフォーマンスについて、まず思うこと
白石 ではまずは、Webパフォーマンスについて思うところをざっくり語っていただけますでしょうか?
竹洞 そうですね、まず総じて言えるのは、Webページのパフォーマンスに悪影響を及ぼしているのは主にサードパーティ(が配信するリソース)だということです。この1年で200以上のWebサイトを見てきましたが、その80%以上で、ページ内のソーシャルボタンや広告など、サードパーティによって配信されるリソースがパフォーマンス悪化の主な原因になっていました。
またちなみに、モバイルネットワークについては、この1年でdocomoさんがだいぶ頑張ったので、ボトルネックがかなり解消されました。最近だと、SoftBankさんの遅延が少し目立ってきている印象です。
白石 なるほど、サードパーティが80%…ちなみに、広告がパフォーマンスのボトルネックになっているとして、それをどうやって改善するんですか?手を付けられない部分なのでは?
竹洞 広告配信側の企業との交渉ですね。
白石 なるほど(笑)
竹洞 とはいえ海外の企業だと、そういう交渉に応じて改善してくれるところはまずありませんが、国内だと改善してくれる場合はありますね。
白石 でも、広告のパフォーマンスが悪いというのは配信側にとっても問題なんじゃないでしょうか?
竹洞 そのはずなんですが、今のところあまりそこに気を払っている業者は少ないです。ただ、海外のアドテク大手である「Criteo」と「SteelHouse」が裁判で争った結果、いろいろな問題のある証拠が出てきたこともあり、アドテク業界そのものがいろいろと見直される時期に来ているのは間違いない。そういう流れの中、広告のパフォーマンスなどについても注目が集まるようになるかもしれません。
川田 アド周りの問題を除くと、フロントエンドのパフォーマンスにおいては、ページのリフローがほとんどと言えるんじゃないかなと思ってます。
矢倉 確かに、動きの少ないサイトとかだと、リフローが主な要因なのは間違いないですね。ただ、JavaScriptを多用したインタラクティブなWebアプリとかだと、そもそも「速い、遅い」の定義をどこに置くかというところから問題になってくると思ってます。
インタラクティブになればなるほど、操作にかける時間や回数が多くなるので、その反応速度なども見るのが重要になるんじゃないかと。、そうなると、リフローもそうですが、画面のペイント処理も問題になることが多いですね。たとえば、動きのある要素が別のレイヤーに分けられておらず、画面全体が再描画されちゃう、そういうことが多いかなあと。
白石 ではそろそろ、具体的なパフォーマンス改善テクニックについて、詳しくお聞きしていきたいと思います。本媒体(HTML5 Experts.jp)はフロントエンドエンジニアが主な読者なのと、インタビュー時間も限られていますので、今回はバックエンドのパフォーマンス改善は取り上げず、フロントエンドとネットワークに絞ってお話をお聞きしたいと思います。
フロントエンドのパフォーマンス改善テクニック
白石 では、フロントエンドのパフォーマンス改善テクニックについてお聞きします。ここについては、以下のような項目についてお聞きしようかと思います。
- リフロー
- ペイント
- スクロール
- DOM
先ほどリフローのお話が出ていましたが、リフローに関してパフォーマンスを改善するというのは、具体的にどのようなことなのでしょうか?
川田 単純に言うと、リフローを発生させないか、発生してしまったとしてもそのコストを減らすということに尽きると思います。
白石 なるほど。それは、width
やheight
と言った、レイアウトに影響のあるプロパティの操作を最小限に抑えるとか、でしょうか。
矢倉 そうですね。最近だと、CSS ContainmentっていうCSSプロパティもあります。これは、リフローなどのレイアウト変更や、描画の範囲を制限するためのプロパティです。 (筆者注: 現在はChromeのみ実装で、実装された際の紹介エントリが詳しい)
川田 あとこれは以前からあるテクニックですが、リフローを発生させることなく要素の見た目を変化させるためにtransform:scale()
などを使用したり、transform:translateZ()
を指定して合成レイヤーを生成したり、でしょうか。
ここらへんは、GPUをいかにうまく活用するか、という話と重なるところがありますね。
白石 GPUの活用、という点で言うと他にもありますか?
川田 will-change
プロパティとかでしょうか。結局、「いかにGPUに載せるか」って話になるんですよね。あと、GPUの活用を始めたブラウザは(意外にも)Internet Explorerだった記憶があります。
(筆者注: will-change
についてはこちらが詳しい)
矢倉 ちょっと違う話ですが、Windows Vistaの動作要件にそれなりの性能を持つGPUが求められていたこともあり、ラップトップでもGPUが普及したという話を聞きました。現在ブラウザでGPUが一般的に使われるようになった遠因とも言えるかもしれませんね。OSとしては、評判良くなかったですが…。
白石 他には、ペイント処理を改善するとしたら、border-radius
やbox-shadow
など、負荷が高いと言われているCSSプロパティの使用をなるべく控える、といったところでしょうか。
(筆者注: border-radius
とbox-shadow
を併用すると遅くなる、というのは有名な話です)
白石 スクロールについてはいかがでしょう?
矢倉 スクロールや初期ロードについては、ユーザーとのインタラクションを阻害して、体験を損ねてしまう「ジャンク」と呼ばれる処理をいかにうまく避けるかが重要になってきます。 そのためのAPIはいくつかあります。
例えば Passive EventListenerは、イベントハンドラ内で preventDefault()
が呼ばれないことをブラウザに伝える機能を addEventListener()
に加えたものですが、これを使用すれば、ブラウザはイベントハンドラの戻りを待たずにスクロール処理を進められます。
(筆者注: JxckさんのPassive Event Listeners によるスクロールの改善という記事が参考になります)。
川田 また、スクロール中に走る処理を throttleする(筆者注: 一定期間内に発生した処理をまとめ、一度の処理としてしまう)とかもやりますね。
矢倉 ほかパララックスなページなどで、scroll
イベントに引っ掛けて何かするという処理は多いですが、結局のところ「特定のDOMが出現したら何かの処理を行う」ということでまかなえることが多い。そんな話があって登場したIntersectionObserver
というAPIを使うと、要素が画面内に入ったことや、要素同士が交差したことを検知できます。
(筆者注: こちらもJxckさんの記事が参考になります: Intersection Observer を用いた要素出現検出の最適化)。
白石 すごく勉強になります、ありがとうございます。ほか、最初に挙げていたトピックとしてはDOMに関する最適化が挙げられますが、ここは何かありますでしょうか?
川田基本的な話としては、Forced Synchronous Layoutを避けるというのがありますね。
白石 ふぉーすどしんく…?なんですか?
川田 Forced Synchronous Layoutっていうのは、例えば要素の幅(width)や高さ(height)、位置など、「レイアウト処理を行ってみないと決まらない」タイプの値をJavaScriptから参照したりすると発生する強制レイアウト処理のことです。できる限りそういう値を参照するのは避けなくてはなりません。
白石 なるほど。ただ経験から言うと、そういうプロパティを参照したくなることって割としょっちゅうあるのではないでしょうか?
矢倉 はい、なので、そういう処理を可能な限り減らすようにコードを書く努力が必要になります。例えば、一度取得した値を変数にキャッシュして使いまわす、とかですね。どのコードの負荷が高いのかを見極めて、意識したプログラミングが重要ですね。
白石 ここまでで、たくさんのJavaScript APIが紹介されていましたね。requestAnimationFrame()
、IntersectionObserver
、Passive EventListenerとか。他にもありますか?
川田 例えば、requestIdleCallback()
とかですかね。これはそれほど優先度の高くない処理を、ブラウザがアイドル状態な時に実行するというものです。
ちなみにこのAPIは、「setImmediate()
が必要かどうか」という議論の中から生まれたものです。ただ、少し用途は違うんですよね。
白石 setImmediate()
は、ただ単に処理をイベントループの末尾に回したいだけですもんね。
川田 ほかは計測系のAPIでしょうか。Navigation Timingとか、Resource Timingとか。こうしたAPIを使えば、JavaScriptからパフォーマンス計測が行えるので、最適化に役立てられます。
ネットワーク関連処理の改善
白石 ではここからは、ネットワーク関連のパフォーマンス・チューニングについて語っていただきたいと思います。まずは転送量の削減についてですが、基本的な項目としては、例えばソースコードのminifyと結合などが挙げられるかと思います。
他には画像の最適化なども含まれると思います。imageminなどを使ってサイズを減らしたり、webp
に変換するなども挙げられますよね。
矢倉 ほか、レスポンシブイメージのテクニックも重要ですね。img
要素のsrcset
属性や、picture
要素とかで、不必要に大きな画像を送らないようにすると。
ただ、最近はスマートフォンの画面のピクセル密度が3倍、4倍とかになっています。そんな端末に合わせて、ばか正直に高解像度の画像を書いてしまうと、転送量も非常に大きくなってしまう。
そうならないようきれいなものを提供したい画像を優先度つけて選ぶとか、あとは可能な限りSVG…ベクター画像を用いるようにも心がけるべきですね。
白石 ベクターといえば、アイコンフォント(筆者注: アイコンをWebフォントとして作成して利用する)もよく使われるテクニックですよね。
矢倉 ただ、最近はアイコンフォントは良くないプラクティスとして認識されつつあります。
白石 え、そうなんですか。
矢倉 テキストコンテンツなので、Webフォントファイルが読み込まれた際にリフローが起こることや、Webフォントのファイルががダウンロードされるまでにタイムラグが大きいことが問題です。 あと、iOS Safariのコンテンツブロッカーには、Webフォントをブロックするものもあるんです。そうなると、アイコンが全く表示されないことになってしまう。
そういうこともあって、SVGスプライトなどをを使うのが望ましいという流れになっています。
白石 そうなんですね…ちなみにWebフォントのダウンロードについてですが、CDNで共有されているものがクライアントのブラウザ上にキャッシュされていることを期待してしまうのですが、実際のところどうなんでしょう?
竹洞 いや、あまり期待できないですね。
白石 ええっ!?
竹洞 基本的には自分のサーバーで配信するのが一番速いです。
白石 そうなんですか!?それは…割とショックです。むしろ「CDNが使えなかったら自分のサーバーからフェッチする」みたいなコードの書き方をしていました。 サードパーティのライブラリも、わざわざファイル結合やモジュールバンドルの対象から外して、CDNから読むようにしていたり。。
竹洞 冒頭で申し上げた「遅延原因の80%がサードパーティ」というのは、そこにCDNも含まれるんです。自前で配信するのが結局一番速い。
白石 なるほど。ではこの流れで竹洞さんに、ネットワーク関係の最適化のお話をもう少し伺いたいと思いますが、DNSに関してはいかがでしょう?
竹洞 DNSに関しては、世界的に遅延している状態です。どのサーバーもパンパンで。Bindのセキュリティ問題などもありますし、DNSは現在大きく問題になっています。 あと、皆さんTTL短すぎなんですよね。5分から10分とかに設定している。
白石 じゃあ、ちょっと長いコンテンツを読んだりしているうちに、DNSのキャッシュ切れちゃってたりすることもあるわけですね。 なんでそんなに短くしてしまうのでしょう?
竹洞 それはやはり、何か問題が生じた時に切り替えられないのが怖いからでしょうね。TTLをみんなが1時間とかに設定するだけで、DNSトラフィックの問題は大きく改善するのですが、難しい問題です。
白石 DNSの話から思い出したのですが、DNSの先行読み込みとか、HTMLで指定できるようになってますよね、たしか。
川田 はい、 <link rel="dns-prefetch" href="<origin>">
という形式で指定できますね。あと、<link rel="prefetch" href="<URL>">
というのもあって、DNSに限らず、任意のリソースをプリフェッチできます。
(筆者注: DNSプリフェッチでウェブページの読み込み速度をスピードアップ、Link prefetching FAQ)
矢倉 あと、プリロードという仕組みもあります。<link rel="preload" href="/styles/other.css" as="style">
のように指定して、リソースの先読みを行えます。
(筆者注: 仕様書によると、prefetchとpreloadの違いは、前者が必ずしも先読みされるわけではなく、低い優先度で先読みされるのに対し、後者は強制的、高い優先度で先読みされる)
矢倉 使うといいと思うのが、CSSファイルに書かれているWebフォントや画像でしょうか。ほとんどの場合、それらのファイルは読み込まれるのがわかっているので、それらをプリロードすると、CSSファイルの読み込みを待たずにダウンロードできます。
白石 他にお聞きしたいことがあるとすると、Service Workerのオフラインキャッシュとかです。これはどんどん使っていくべきでしょうか?
矢倉 自分自身まだ使ったことがないので、あまり語れませんが…Progressive Web Appsの文脈で、App Shellという、アプリの「ガワ」をService Workerにキャッシュさせて、最初の描画(First Paint)を速めようという話がありますね。
川田 ただ、キャッシュの更新方法についてもきちんと設計しないと酷いことになりますよね。Webアプリケーションにおけるデプロイを、Service Worker側でどのように扱うのかという問題。サーバー上のリソースが更新されたときに、ブラウザ側へどのように反映するのか。複雑化は避けられないですよね。
白石 確かに。それもあって、ぼくらも自分たちのサービスでは、Service Workerのキャッシュを利用するコードまでは用意したのですが、結局有効化してないです。
最後に、竹洞先生からひとこと
白石 では、だいぶお時間もかかってしまったので、まだまだお聞きしたいことはあるのですが、そろそろ締めに入りたいと思います。最後に、読者の皆様に伝えておきたいこととかありますか?
竹洞 じゃあ、最後に一つだけ。サーバの転送量削減についての目安ですが…。
白石 はい、お願いします。
竹洞 モバイルだと、全体で200kbを超えるページは、配信に3秒以上かかってしまいます。転送量削減については、これを目安にするのはいいのではないかと思います。
白石 えっ、200kbですか?クライアントにキャッシュされているリソースがあることは期待していいんですか?
竹洞 いえ、キャッシュなしで。先ほどもありましたが、モバイルだと、あんまりキャッシュが期待できないので。
ほか全員: (マジか…つらい…)
竹洞 頑張ってくださいね(ニッコリ)