2015年11月12日木曜日
Steve Souders
パフォーマンスを改善するには、まず計測する必要があります。 しかし、何を計測すべきでしょうか?
パフォーマンス業界で最も利用されている指標は「page load time」(例として「window.onload」や「document complete」)です。
Web 1.0時代は、ページがシンプルだったため、読み込み時間は各ユーザーが新しいWebページ(複数ページのサイト)を読み込んだときのUX(ユーザーエクスペリエンス)とほぼ同じでした。
しかし、Web 2.0とSPA(シングルページアプリケーション)の時代には、ページの読み込み時間は、もはやユーザーが見ているものとは違ったものになっています。いい例としてGmailとAmazonを比較するとわかりやすいでしょう。
ここ数年で、Start renderやSpeed Indexなど、Page load timeと関連する指標の人気が高まってきました。しかし、これらの指標は欠点を抱えています。それは、ユーザーがそのページでもっとも関心がある内容が何か分からないことです。
コンテンツはすべて同じ、と評価するパフォーマンス指標はいい指標ではない。
ユーザーは、ページ内のすべてに等しい価値を持っているわけではありません。 代わりに、ユーザーは、商品イメージやナビゲーションバーなど、ページ内の1つ、または複数の重大なデザイン要素に注目します。優れたパフォーマンスメトリックを探すには、ユーザーが重要とするコンテンツを待つ時間を計測してみることです。ブラウザ自身はどのコンテンツが重要かわかりません。サイトオーナーは、パフォーマンス指標をその場所に適切に置く必要があります。 そのためには、ユーザータイミングでカスタムメトリックを作成することが必要です。
ユーザータイミング仕様
W3C User Timing仕様では、Webアプリケーションにカスタムメトリックを追加するためのAPIを提供しています。 これは、performance.markとperformance.measureという2つの主要機能を介して行われます。
- performance.markは、navigationStartからの時間(ミリ秒単位)を記録します。
- performance.measureは2つのマークの間にデルタを記録します
ほかにUser Timing APIもあります。しかし、マークと計測が主な機能です。 自社ページがマークと計測でいっぱいなら、それらを追跡できるように、どのようにメトリックを集めるかという疑問が沸いてきます。幸いなことにユーザータイミングは公式のW3C仕様なので、多くのサービスは、自動的にダッシュボードにこれらの指標を抽出してレポートします。 これは、SOASTAの mPulse、WebPageTest、およびSpeedCurveに当てはまります。
カスタムメトリックのサンプル
User Timing APIはシンプルですが、マークと計測をいつどこで取得できるかを見つけるのが難しい場合があります。 これは、ブラウザの動作の複雑さ、主にスタイルシートと同期スクリプトが解析とレンダリングをブロックしてしまうことが原因です。理解を深めるために、カスタム・メトリックの例をいくつか見てみましょう。
ブラウザ互換性に関する簡単なメモ:ユーザータイミングは、Safari以外のほとんどのブラウザで利用できます。 下のサンプルでは、ネイティブAPIを使用していますが、ライブコードにおいては互換性をチェックし、 shim またはwrapperを使用する必要があります。good polyfillはPat Meenanのサイトから入手できます。
※Polyfillとは、一種のライブラリ。「標準となったメソッドが存在しない場合に、自分で供給してしまう方法」
事例1 スタイルシートによるブロックの実行
スタイルシートを読み込むと、ページ全体がレンダリングからブロックされます。 これは、すべてのブラウザと、動的に読み込まれるスタイルシートに当てはまります。 そのため、スタイルシートがいつブロックされるか知っておけば、なぜレンダリングが要求より遅くなるかを理解することができます。 カスタムメトリックを取得するためのサンプルコードは次のとおりです。
1 2 3 4 5 |
<link rel=”stylesheet” href=”/sheet1.css”> <link rel=”stylesheet” href=”/sheet4.css”> <script> performance.mark(“stylesheets done blocking”); </script> |
このスニペットの鍵は、ページ内のすべてのスタイルシートLINKタグの下に配置されていることを確認することができます。 これは、ほとんどのスタイルシートがページの上部に表示されるので、合理的な要件です。
- インラインスクリプトは、以前のすべてのスタイルシートをダウンロードし、解析され、ページに適用されるまで実行されません。 したがって、LINKタグの後にインラインスクリプトを配置すると、すべてのブロックCSSが処理された後にマークが設定されます。
- スタイルシートに@importが含まれている可能性があります。たとえば、sheet1.cssによってsheet2.cssとsheet3.cssがダウンロードされ、解析され、適用されるなど、他のスタイルシートが取り込まれる可能性があります。 そのため、スタイルシートの読み込み時間を追跡するだけでは(リソースタイミングなど)は不十分です。 そうすると、測定値が短すぎてスタイルシートのブロックの影響が過小評価されることになります。
優れた(高速な)ユーザーエクスペリエンスを生み出すには、Webアプリケーションのレンダリングを妨げているものを見つけることが重要です。 可能性の高い原因は、スタイルシートと同期スクリプトですが、遅延レンダリングの原因を明確にするのは困難です。 “stylesheets done blocking”というカスタムメトリックは便利です。なぜなら、 “stylesheets done blocking”の後にレンダリングがうまく動くと、この調査は同期スクリプトに焦点を当てればいいわけです。これは次の例につながります。
事例2 スクリプトブロックの実行
同期スクリプトは、それ以降の全DOM要素のレンダリングをブロックするので、スクリプト・ブロックが完了した時点を調べてみましょう。 この測定値を取得するためのスニペットを次に示します。
1 2 3 4 5 |
<script src=”a.js”></script> <script src=”b.js” async></script> <script> performance.mark(“scripts done blocking”); </script> |
インラインスクリプト(performance.markを呼び出す)は、スクリプトa.jsがダウンロード、解析、実行された後に実行されることが保証されています。 一方、b.jsにはasync属性があるため、b.jsが読み込まれる前にマークが記録されます。 この動作は、a.jsがレンダリングをブロックするが、b.jsはレンダリングをブロックしないため、スクリプトのブロックを測定するという目標と一致します。
正確な “スクリプトがブロックされた”メトリックを取得するのは、スタイルシートの対応よりも少し難しいでしょう。 それはスタイルシートと同様に、ユーザータイミング・マークをすべての同期スクリプトの下に配置する必要があるからです。 これは、ウェブサイトが頻繁にスクリプトをページ全体に振りかけるため困難になる場合があります。 この場合、マーク測定は以前のDOM要素がレンダリングされた後に行われ、レンダリングの開始時には正確に測定されません。
したがって、この例は、HEADに同期スクリプトを持つページでのみ有効です。 このような制限があっても、このカスタムメトリックは、構文解析や実行のためにブロックタイムがダウンロードタイムよりも長い大きなスクリプトを持つページに非常に役立ちます。 これらの種類のページのウォーターフォールを見ると、すべてのスクリプトのダウンロードは終了しますが、レンダリングが始まるまでにはまだ長いギャップがあります。 「スクリプトブロックの実行」マークを付けると、長い解析時間と実行時間をレンダリングブロックの原因として特定できます。
事例3 フォントの読み込み
カスタムフォントを使用している場合は、フォントファイルをダウンロードするまで、一部のブラウザでは、そのフォントを使用するテキストが表示されないことに注意してください。 他のブラウザでは、テキストはシステムのデフォルトフォントでレンダリングされ、カスタムフォントが読み込まれると再レンダリング(「フラッシュ」)されます。 つまり「フォントが読み込まれた」カスタムメトリックは、テキスト要素がレンダリングされたときを示す重要なポイントです。
現在、フォントの “onload”のイベントはありません。 しかし、フォントを観察しロードタイミングを決定する技術があります。たとえば、フィラメントグループのScott Jehlによるフォントイベントで再読み込みされたFont Loadingや、TypekitやGoogleフォントで使用されるWeb Font Loaderなどがあります。 これらを使用して、フォントの読み込みが完了したことを追跡し、その時点で “font loaded”カスタムメトリックを設定するためのperformance.markの呼び出しを含めることができます。
事例4 ヒーローイメージ
「ヒーローイメージ」は、ユーザーエクスペリエンスにおいて、ページ内で最も重要な要素です。 Hero Image Custom Metricsの記事では、画像のレンダリング時に測定する画期的な手法について説明しています。 スニペットは次のようになります。
1 2 3 4 5 6 |
<img src=”hero.jpg” onload=”performance.clearMarks(‘img displayed’); performance.mark(‘img displayed’);”> <script> performance.clearMarks(“img displayed”); performance.mark(“img displayed”); </script> |
画期的なのは、イメージ・オンロード・マークとインライン・スクリプト・マークのうち大きい方を取ることです。 これは、画像が実際にレンダリングされるときを正確に捕捉します。 イメージのロードマークは、イメージのダウンロードにブロッキングリソースよりも時間がかかるレンダリング時間を反映します。 インラインスクリプトマークは、イメージがすぐにダウンロードされるレンダリング時間を反映しますが、リソースをブロックするとレンダリングされません。 多くの場合、ページ内の最も重要なコンテンツにはイメージが含まれるため、このスニペットはさまざまなカスタムメトリックに役立ちます。
事例5 テキストの段落
意外なことに、最も困難なカスタムメトリックの1つは、テキストがいつ表示されるかを判断することです。 スタイルシートや同期スクリプトによってブロックされることに加えて、ダウンロードに時間がかかるカスタムフォントを使用すると、テキスト要素(P、SPAN、LI、DIVなど)をレンダリングからブロックすることもできます。
テキスト要素がカスタムフォントを使用しない場合、テキスト要素の後ろにインラインスクリプトを置くだけで、レンダリングされる時間が取得されます。
1 2 3 4 |
<p>This is the call to action text element.</p> <script> performance.mark(“text displayed”); </script> |
テキストがカスタムフォントを使用する場合、レンダリング時間はインラインスクリプトのマークとフォントロードマークの最大値です(前の例を参照)。 ここで重要なのは、「テキスト表示」を2回記録するヒーローイメージに似たパターンを使用することです。1回はフォント読み込みスニペットで、もう1度はテキスト要素の後のインラインスクリプトで行います。 さらに、新しいマークを設定する前に同じ名前の以前のマークをクリアし、テキストが表示されたときに適切に反映される2つの値の最大値を確実に取得します。
事例6 シングルページアプリ
ページの読み込み時間をメトリックとして使用することの欠点の1つは、単一のページアプリ(SPA:シングルページアプリ)内でのユーザのアクションを測定しないことです。onloadは1回だけ起動しますが、ユーザーが複数のアクションを実行する可能性があり、それぞれを測定する必要があります カスタムメトリックはこの問題を解決しますが、適切な開始時刻を作成するという責任を負う必要があります。
サンプルコードを表示するには、「更新」ボタンがあるSPAを見つけます。 クリックすると、新しいデータを取得するためにXHRまたはJSONリクエストが起動します。 返されたデータは、DOMを変更するupdateData()関数に渡されます。 このSPAユーザーアクションを測定するサンプルコードは次のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<input type=button value=”Update” onclick=”fetchData()”> <script> function fetchData() { performance.clearMarks(“start update”); performance.mark(“start update”); // Do an XHR or JSON request that calls updateData() with the new data.} function updateData(data) { // Update the DOM with the new data. performance.clearMarks(“finish update”); performance.mark(“finish update”); perforance.measure(“update data”, “start update”, “finish update”);} </script> |
「更新開始」のマークはSPAアクションの最初のJavaScriptであり、「更新終了」はSPAアクションで最後に実行されたJavaScriptなので注意してください。 これにより、XHRやJSONのダウンロードやDOMの更新など、SPAアクション全体が確実に測定されます。 また、初めてperformance.measureを使用することにも注意してください。 これは、navigationStart以外の開始時刻に相対的なデルタを取得するためです。
SpeedCurveのカスタムメトリックス
ユーザータイミングでカスタムメトリックを構築することの大きな利点は、多くのパフォーマンスメトリックサービスがパフォーマンスタイミングダッシュボードに表示するユーザータイミングマークと指標が自動的に抽出されることです。 WebPageTestはこれを行っており、SpeedCurveはWebPageTestの上に構築されているため、同様に実行されます。
SpeedCurveでカスタムメトリックを確認するには、設定に名前を付けます。 speedcurve.comのウェブサイトでは、「heromark」というカスタムメトリックを使用して、ヒーローイメージ(通常はページ上部の図)がいつ表示されるかを測定します。 設定の下にあるフォームを使用して、「Hero Mark」という名前を付けることができます。
SpeedCurve設定に複数のカスタムメトリックを追加できます。 一部のページにはサードパーティのスクリプトで設定されたマークやメジャーを含む多数のマークとメジャーが含まれているため、最も重要なカスタムメトリックだけが表示されます。
カスタムメトリックは、さまざまなSpeedCurveダッシュボードに表示されます。 たとえば、サイトのダッシュボードには、ヒーローマークのカスタムメトリックのトレンドラインが表示されます。
ウォーターフォールチャートでカスタムメトリックを確認することは非常に重要です。 これにより、カスタムメトリックが適切なものを測定している場合に「デバッグ」することができます。 たとえば、speedcurve.comの下のウォーターフォールでは、前述のすべてのスクリプト(オレンジ色)とスタイルシート(緑色)がダウンロードされるまで、「ヒーローマーク」のカスタムメトリックがブロックされていることがわかります。 下のサムネイルを使用すると、ヒーローイメージ(グラフ)が実際に表示されていることが確認されます。
あなたほど、あなたのウェブサイトをよく知っている人はいません。したがって、自分とユーザーが最も関心を持つコンテンツを測定するカスタム・メトリックを追加することが重要です。これにより、ユーザーが合成(シンセティック)とRUMの両方で経験していることの速度を追跡できます。
ご質問やお問合せは、[email protected] までよろしくお願いします。