Google アナリティクスで重要なパフォーマンス指標を測定する

この Codelab では、Google アナリティクスUser Timings API を使用して、ウェブサイトやアプリの実際のパフォーマンスを測定し、ユーザー エクスペリエンスを向上させるために最適化する方法を学びます。

WebPagetest.org などのツールは、パフォーマンスの最適化を始めるには最適ですが、サイトのパフォーマンスの真のテストは、常に実際のユーザーからの実際のデータになります。

ウェブサイトを運営している場合は、Google アナリティクスを使用してトラフィックやデバイス、ブラウザの使用状況などを測定している可能性が高いでしょう。少しコードを追加するだけで、パフォーマンス指標を組み込むことができます。

学習内容

  • User Timings API を使用してパフォーマンス指標を正確かつ効果的に測定する方法
  • レポートに組み込めるように、そのデータを Google アナリティクスに送信する方法

必要なもの

  • デベロッパー コンソールを備えたブラウザ
  • Web Server for Chrome、または任意のウェブサーバーを使用する
  • サンプルコード
  • テキスト エディタ
  • (省略可)Google アナリティクス アカウント

このチュートリアルの利用方法をお選びください。

通読するのみ 通読し、演習を行う

ウェブサイトやアプリケーションの作成経験についてお答えください。

初心者 中級者 上級者

すべてのサンプルコードをパソコンにダウンロードできます。

ZIP をダウンロード

または、コマンドラインから GitHub リポジトリのクローンを作成します。

git clone https://github.com/googlecodelabs/performance-analytics.git

サンプルコードは、この Codelab の番号付きステップに対応するサブディレクトリに分割されています。これを使用すると、コードラボ内を簡単に移動したり、実装が正しいことを確認したりできます。

差分プログラムにアクセスできる場合は、それを使用してステップごとに何が変更されたかを確認できます。

このコードラボでは、次のアセットを読み込む単一の HTML ファイルを使用します。

  • ウェブフォント
  • スタイルシート
  • 画像
  • JavaScript

これらのアセットタイプごとに主要なパフォーマンス指標を測定する新しいコードを記述します。

アセットのパフォーマンスに関する考慮事項

パフォーマンスの最適化について読んだことがある方なら、これらのアセットタイプにはそれぞれ独自の癖があり、全体的なパフォーマンスの認識にさまざまな影響を与える可能性があることをご存じでしょう。

CSS

たとえば、スタイルシートは、その後に続く DOM 内のすべての要素のレンダリングをブロックします。つまり、ブラウザは、スタイルシートのリクエスト、ダウンロード、解析を行ってから、その後に続く DOM 内のコンテンツをレンダリングする必要があります。そのため、通常はドキュメントの <head> にスタイルシートを配置することをおすすめします。また、CSS はブロックの性質を持つため、重要な CSS のみを <head> に配置し、重要でない CSS は後で非同期で読み込むことが推奨されることもよくあります。

JavaScript

一方、JavaScript はレンダリングをブロックしませんが、DOM の解析と構築をブロックします。これは、JavaScript が DOM を変更できるためです。つまり、ブラウザが <script> タグ(非同期スクリプトを除く)を検出するたびに、次のタグに進む前にコードを実行する必要があります。<script> タグが外部 JavaScript ファイルを参照している場合、ブラウザはコードをダウンロードして実行してから次の処理に進む必要があります。

そのため、JavaScript は </body> 終了タグの直前に読み込むことが推奨されることが多く、DOM の大部分をできるだけ早く利用できるようにします。

ウェブフォント

ウェブフォントを読み込む場合は、フォントが使用可能になるまでドキュメントのレンダリングをブロックすることもできます。この場合、ユーザーが実際にどのくらいの時間を要しているかを把握することが重要です。ウェブフォントは、サイトにアクセスするユーザーのほとんどにとって読み込みに時間がかかる可能性があります。そのため、測定と実際のデータに基づく意思決定が非常に重要になります。

画像

最後に、画像はサイトを生き生きと見せる効果がありますが、読み込みに最も時間がかかることもよくあります。この意味を理解し、使用パターンとページ読み込み時間の相関関係を見つけることが、最適化の方法を理解するうえで重要です。

このコードラボの最初のステップでは、パフォーマンス測定コードを追加する前のデモページの様子を確認します。

デモを表示するには、新しいフォルダを作成し、その中に index.html というファイルを追加します。次のコードをコピーして index.html ファイルに貼り付けます。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Performance Analytics Demo</title>

  <!-- Start fonts -->
  <link href="https://fonts.googleapis.com/css?family=Roboto:400,700,400italic" rel="stylesheet">
  <!-- End fonts -->

  <!-- Start CSS -->
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
  <style>
    body { font-family: Roboto, sans-serif; margin: 1em; }
    img { float: left; height: auto; width: 33.33%; }
    .gallery { overflow: hidden; }
  </style>
  <!-- End CSS -->

</head>
<body>

  <div class="container">

    <!-- Start images -->
    <div class="gallery">
      <img src="http://lorempixel.com/380/200/animals/1/">
      <img src="http://lorempixel.com/380/200/animals/2/">
      <img src="http://lorempixel.com/380/200/animals/3/">
    </div>
    <!-- End images -->

    <h1>Performance Analytics Demo</h1>
    <p>Real performance data from real users.</p>

  </div>

  <!-- Start JavaScript -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
  <!-- End JavaScript -->

</body>
</html>

次に、Web Server for Chrome を開き、作成したディレクトリでローカル サーバーを起動します。[automatically show index.html] がオンになっていることを確認します。

Screen Shot 2016-05-11 at 1.03.43 PM.png

これで、ブラウザで http://127.0.0.1:8887/ に移動して、デモファイルを表示できるようになります。次のようになります。

Screen Shot 2016-05-11 at 10.59.03 AM.png

デモページが実行されたら、コードを見て、読み込まれているさまざまなアセットタイプを確認してください。次の手順では、これらのアセットが読み込まれ、ユーザーが操作できるようになったタイミングを測定するコードを追加します。

前述のアセットのパフォーマンスに関する考慮事項のセクションで説明したように、CSS は DOM 要素のレンダリングと、DOM 内で CSS の後に続くスクリプトの実行をブロックします。

作成したデモファイルには、Bootstrap といくつかのインライン スタイルを参照する次の CSS が含まれています。

<!-- Start CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<style>
  body { font-family: Roboto, sans-serif; margin: 1em; }
  img { float: left; height: auto; width: 33.33%; }
  .gallery { overflow: hidden; }
</style>
<!-- End CSS -->

CSS は DOM 要素のレンダリングとスクリプトの実行の両方をブロックするため、現在の時刻を保存する <script> タグを CSS の直後に追加することで、CSS によるブロックが終了したタイミングを特定できます。

変数を作成して new Date() を割り当てることで実現できますが、User Timings API を使用すると、performance.mark メソッドというはるかに簡単な方法があります。

CSS がレンダリングとスクリプト実行の両方をブロックし終わったタイミングをマークするには、終了 <!-- End CSS --> コメントの直前に次のコード行を追加します。

<script>performance.mark('css:unblock');</script>

performance.mark メソッドは、この時点の高解像度タイムスタンプを作成し、メソッドに渡された名前に関連付けます。この例では、マークに「css:unblock」という名前を付けました。

performance.mark メソッドは performance.measure メソッドと密接に関連しています。performance.measure メソッドは、2 つのマーク間の時間差を計算するために使用されます(マークは自分で付けるだけでなく、Navigation Timing API のさまざまなポイントに対してブラウザが自動的に付けるマークも使用できます)。

次のユーティリティ関数は、追加したマークと Navigation Timing API によって作成された responseEnd マークの間の時間間隔を測定して返します。

function measureDuration(mark, opt_reference) {
  var reference = opt_reference || 'responseEnd';
  var name = reference + ':' + mark;

  // Clears any existing measurements with the same name.
  performance.clearMeasures(name);

  // Creates a new measurement from the reference point to the specified mark.
  // If more than one mark with this name exists, the most recent one is used.
  performance.measure(name, reference, mark);

  // Gets the value of the measurement just created.
  var measure = performance.getEntriesByName(name)[0];

  // Returns the measure duration.
  return measure.duration;
}

このユーティリティ関数を使用するには、perf-analytics.js という名前の新しいファイル(index.html ファイルと同じディレクトリ)を作成し、上記のコードをコピーして貼り付けます。

この関数が定義されたので、この関数を呼び出して「css:unblock」マーク名を渡すことができます。他のリソースの読み込みを妨げないように、これらの測定はウィンドウの読み込みイベントが発生するまで延期する必要があります。

このコードを呼び出す関数を作成すると、perf-analytics.js ファイルは次のようになります。

window.onload = function() {
  measureCssUnblockTime();
};


/**
 * Calculates the time duration between the responseEnd timing event and when
 * the CSS stops blocking rendering, then logs that value to the console.
 */
function measureCssUnblockTime() {
  console.log('CSS', 'unblock', measureDuration('css:unblock'));
}


/**
 * Accepts a mark name and an optional reference point in the navigation timing
 * API and returns the time duration between the reference point and the last
 * mark (chronologically).
 * @param {string} mark The mark name.
 * @param {string=} opt_reference An optional reference point from the
 *     navigation timing API. Defaults to 'responseEnd'.
 * @return {number} The time duration
 */
function measureDuration(mark, opt_reference) {
  var reference = opt_reference || 'responseEnd';
  var name = reference + ':' + mark;

  // Clears any existing measurements with the same name.
  performance.clearMeasures(name);

  // Creates a new measurement from the reference point to the specified mark.
  // If more than one mark with this name exists, the most recent one is used.
  performance.measure(name, reference, mark);

  // Gets the value of the measurement just created.
  var measure = performance.getEntriesByName(name)[0];

  // Returns the measure duration.
  return measure.duration;
}

最後に、index.html から perf-analytics.js スクリプトを読み込む必要があります。そのためには、メイン ドキュメントに次のスクリプトタグを追加します。他のリソースの読み込みを妨げないように、最後に必ず追加してください。

<!-- Start performance analytics -->
<script async src="perf-analytics.js"></script>
<!-- End performance analytics -->

この手順を完了すると、コードは Codelab リポジトリの 01-css ディレクトリの内容と一致するはずです。

ブラウザでページを読み込み、デベロッパー コンソールを開くと、次のような出力が表示されます。

Screen Shot 2016-05-17 at 11.13.02 AM.png

ウェブフォントは通常、最初のデモファイルで確認できるように、外部スタイルシートを介して読み込まれます。

<!-- Start fonts -->
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700,400italic" rel="stylesheet">
<!-- End fonts -->

これは CSS ファイルへの <link> タグであるため、フォントが読み込まれて使用できる状態になったタイミングを特定するには、ステップ 1 のように <link> の直後の <script> タグ内にマークを追加するだけでよいと思われるかもしれません。

残念ながら、そう簡単ではありません。

スタイルシートの内容は CSSOM の構築に使用されるため、スタイルシートは JavaScript の実行をブロックします。読み込まれる JavaScript が CSSOM にアクセスする必要がある可能性があるため、CSSOM が完全に構築されるまで実行を遅らせる必要があります。

ただし、ブラウザはフォントを実際にダウンロードしなくても CSSOM を構築できます。つまり、フォントのスタイルシートの <link> タグの直後にインライン スクリプトタグで DOM にマークを追加すると、フォントが完全に読み込まれるにマークが付けられる可能性があります。

ブラウザでフォント読み込みイベントが利用可能になるまでは、JavaScript を使用して、フォントが実際にアクティブになり、ページで使用できる状態になったタイミングを判断する必要があります。幸いなことに、JavaScript 経由でフォントを読み込むことは、CSS ファイルへの追加のブロック リクエストを必要としないため、パフォーマンスの向上にもつながります。

ほとんどのウェブフォント(Google Fonts、Typekit、font.com のフォントなど)は、Google と Typekit が共同で開発した webfont.js スクリプトを使用して読み込むことができます。

webfont.js を使用してフォントを読み込むようにメイン ドキュメントを更新するには(<link> タグではなく)、コードのフォント セクションを次のコードに置き換えます。

<!-- Start fonts -->
<script>
window.WebFontConfig = {
  google: {families: ['Roboto:400,700,400italic']},
  timeout: 10000,
  active: function() {
    performance.mark('fonts:active');
  }
};
</script>
<script async src="https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js"></script>
<!-- End fonts -->

上記のコードについては、次の 2 つの重要な点に注意してください。

  • アクティブ コールバックで「fonts:active」マークを作成するため、後でフォントの読み込みにかかった時間を測定できます。
  • webfonts.js を読み込む <script> タグに async 属性が含まれているため、ドキュメントの残りの部分の解析やレンダリングがブロックされることはありません(<link> タグではブロックされます)。

上記のコードで「fonts:active」マークは作成されますが、このマークを測定してコンソールに記録することは、「css:unblock」マークの場合ほど簡単ではありません。これは、フォントの読み込みが非同期で行われるようになったためです。window.onload ハンドラで「fonts:active」マークを測定しようとすると(「css:unblock」で行ったように)、フォントがまだ読み込まれていない可能性があります。

この問題を解決するには、フォントが読み込まれたら解決される Promise を作成します。次の関数は、この処理を行います。このコードをコピーして perf-analytics.js ファイルに貼り付けます。

/**
 * Creates a promise that is resolved once the web fonts are fully load or
 * is reject if the fonts fail to load. The resolved callback calculates the
 * time duration between the responseEnd timing event and when the web fonts
 * are downloaded and active. If an error occurs loading the font, this fact
 * is logged to the console.
 */
function measureWebfontPerfAndFailures() {
  new Promise(function(resolve, reject) {
    // The classes `wf-active` or `wf-inactive` are added to the <html>
    // element once the fonts are loaded (or error).
    var loaded = /wf-(in)?active/.exec(document.documentElement.className);
    var success = loaded && !loaded[1]; // No "in" in the capture group.
    // If the fonts are already done loading, resolve immediately.
    // Otherwise resolve/reject in the active/inactive callbacks, respectively.
    if (loaded) {
      success ? resolve() : reject();
    }
    else {
      var originalAciveCallback = WebFontConfig.active;
      WebFontConfig.inactive = reject;
      WebFontConfig.active = function() {
        originalAciveCallback();
        resolve();
      };
      // In case the webfont.js script fails to load, always reject the
      // promise after the timeout amount.
      setTimeout(reject, WebFontConfig.timeout);
    }
  })
  .then(function() {
    console.log('Fonts', 'active', measureDuration('fonts:active'));
  })
  .catch(function() {
    console.error('Error loading web fonts')
  });
}

また、この新しい関数を呼び出すように window.onload ハンドラを更新します。

window.onload = function() {
  measureCssUnblockTime();
  measureWebfontPerfAndFailures();
};

この手順を完了すると、コードは Codelab リポジトリの 02-fonts ディレクトリの内容と一致するはずです。

ブラウザでページを読み込み、デベロッパー コンソールを開くと、次のような出力が表示されます。

Screen Shot 2016-05-17 at 11.13.22 AM.png

画像が表示されているかどうかを判断するのは、見た目ほど簡単ではありません。前の手順で、スタイルシートと同期 <script> タグがレンダリング、解析、スクリプトの実行をブロックする可能性があることを学びました。どちらもブラウザのプリロード スキャナをブロックしないことは、あまり知られていないかもしれません。

プリロード スキャナは、パフォーマンスを重視しないサイトでも、ブロッキング アセットを多く含むサイトでも、パフォーマンスを改善するための試みの一つとして、すべての最新ブラウザに実装されています。一部のアセットは解析、レンダリング、スクリプトの実行をブロックする可能性がありますが、ダウンロードをブロックする必要はありません。そのため、ブラウザは DOM の構築を開始する前に HTML ファイルをスキャンし、すぐにダウンロードを開始できるアセットを探します。

画像に関しては、DOM に追加されるまでに画像がダウンロードされている可能性が十分にあります。つまり、画像がダウンロードされた時点は、必ずしもパフォーマンス指標として適切とは限りません。パフォーマンス指標として重要なのは、ユーザーに画像が表示されるタイミングです。

DOM に追加される前に画像がダウンロードされる場合、画像が表示されるのは DOM に追加された時点です。一方、画像が DOM に追加される前にダウンロードされていない場合、画像が表示されるのは onload ハンドラが発火したときです。

そのため、画像が表示されるタイミングを把握するには、両方のケースを処理する必要があります。

これを行うには、各画像の onload ハンドラと、DOM の最後の画像の直後のインライン <script> タグにマークを追加します。最後に発生したマークが、すべての画像が表示された時点を表すマークになります。

画像が読み込まれたときとレンダリングされたときの両方のマークを追加するには、index.html の画像コードを次のように更新します。

<!-- Start images -->
<div class="gallery">
  <img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
  <img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
  <img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
</div>
<script>performance.mark('img:visible')</script>
<!-- End images -->

特定のマーク名の performance.measure メソッドは常に最後のマークを使用するため(同じ名前のマークが複数見つかった場合)、perf-analytics.js ファイルの measureDuration ユーティリティ関数をそのまま使用できます。

/**
 * Calculates the time duration between the responseEnd timing event and when
 * all images are loaded and visible on the page, then logs that value to the
 * console.
 */
function measureImagesVisibleTime() {
  console.log('Images', 'visible', measureDuration('img:visible'));
}

上記の関数を perf-analytics.js ファイルに追加し、それを呼び出すように window.onload ハンドラを更新します。

window.onload = function() {
  measureCssBlockTime();
  measureWebfontPerfAndFailures();
  measureImagesVisibleTime();
};

この手順を完了すると、コードは Codelab リポジトリの 03-images ディレクトリの内容と一致するはずです。

ブラウザでページを読み込み、デベロッパー コンソールを開くと、次のような出力が表示されます。

Screen Shot 2016-05-17 at 11.13.39 AM.png

async 属性のない <script> タグは、ダウンロードと実行が完了するまで DOM の解析をブロックするため、DOM 内の最後の同期 <script> の直後のインライン スクリプトタグにマークを追加することで、すべてのスクリプトの実行が完了した時点を特定できます。

この場合、ブラウザはスクリプトを読み込んだ後に実行する必要があり、時間がかかるため、onload ハンドラを使用しても機能しません。読み込みは速いが実行が遅いスクリプトは、読み込みが遅いスクリプトと同じくらい悪い可能性があります。

メイン ドキュメント内のすべての JavaScript が読み込まれて実行されたタイミングをトラッキングするには、index.html の JavaScript セクションを次のコードで更新します。

<!-- Start JavaScript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script>performance.mark('js:execute');</script>
<!-- End JavaScript -->

これにより、jQuery と Bootstrap のプラグインのスクリプトがダウンロードされ、実行が完了した直後に「js:execute」という名前のマークが追加されます。

この処理にかかる時間を測定するには、次の関数を perf-analytics.js に追加します。

/**
 * Calculates the time duration between the responseEnd timing event and when
 * all synchronous JavaScript files have been downloaded and executed, then
 * logs that value to the console.
 */
function measureJavaSciptExecutionTime() {
  console.log('JavaScript', 'execute', measureDuration('js:execute'));
}

次に、window.onload ハンドラから呼び出します。

window.onload = function() {
  measureCssBlockTime();
  measureWebfontPerfAndFailures();
  measureImagesVisibleTime();
  measureJavaSciptExecutionTime();
};

この手順を完了すると、コードは Codelab リポジトリの 04-javascript ディレクトリの内容と一致するはずです。

ブラウザでページを読み込み、デベロッパー コンソールを開くと、次のような出力が表示されます。

Screen Shot 2016-05-17 at 11.14.03 AM.png

すべてのブラウザが JavaScript の Promise や User Timing API をサポートしているわけではありません。これらの機能のいずれかをサポートしていないブラウザで、これまでに作成したコードを実行すると、エラーが発生します。

これに対処するには、機能検出を使用します。フォント セクションの直前に次のコードを追加します。この JavaScript の行は performance.mark メソッドのサポートを検出するため、このメソッドが使用される前にページに追加する必要があります。

<!-- Start feature detects -->
<script>window.__perf = window.performance && performance.mark;</script>
<!-- End feature detects -->

次に、performance.mark を呼び出す index.html の任意の場所で、機能検出を接頭辞として追加します。次の例は、画像ブロックのコードを更新するものです。他のセクションも必ず更新してください。

<!-- Start images -->
<div class="gallery">
  <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
  <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
  <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
</div>
<script>__perf && performance.mark('img:visible')</script>
<!-- End images -->

window__perf 変数が設定されたので、perf-analytics.js でもこの変数を使用して、現在のブラウザでサポートされていないメソッドを呼び出さないようにすることができます。

measureDuration 関数を更新して、次の条件を追加する必要があります。

function measureDuration(mark, opt_reference) {
  if (window.__perf) {
    // ...
  }
}

最後に、すべてのブラウザが JavaScript の Promise をサポートしているわけではないため、measureWebfontPerfAndFailures 関数も条件付きでラップする必要があります。

function measureWebfontPerfAndFailures() {
  if (window.Promise) {
    // ...
  }
}

これで、どのブラウザでも問題なくコードを実行できるようになります。

この手順を完了すると、コードは Codelab リポジトリの 05-feature-detects ディレクトリの内容と一致するはずです。

この Codelab の最後のステップでは、コンソールに記録されているデータを取得し、代わりに Google アナリティクスに送信します。また、Google アナリティクスにデータを送信する前に、analytics.js ライブラリデフォルトのトラッキング スニペットをページに追加する必要があります。

メインの JavaScript ブロックの後、perf-analytics.js スクリプトが読み込まれる前に、index.html に次のコードを追加します。

<!-- Start analytics tracking snippet -->
<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
</script>
<script async src="https://www.google-analytics.com/analytics_debug.js"></script>
<!-- End analytics tracking snippet -->

ウェブサイトに Google アナリティクスを追加したことがある場合は、「UA-XXXXX-Y」プレースホルダを、Google アナリティクスで新しいプロパティを作成したときに受け取ったトラッキング ID に置き換える必要があることをご存じでしょう。

analytics.js トラッキング スニペットは、次の 4 つの主要な処理を行います。

  • analytics.js JavaScript ライブラリをダウンロードする非同期の <script> 要素を作成します。
  • グローバル ga() 関数(ga() コマンドキュー)を初期化します。analytics.js ライブラリが読み込まれ、準備が完了した段階でコマンドが実行されるようスケジュール設定できます。
  • 'UA-XXXXX-Y' パラメータで指定したプロパティに新しいトラッカー オブジェクトを作成するコマンドを ga() コマンドキューに追加します。
  • 現在のページのページビューを Google アナリティクスに送信する別のコマンドを ga() コマンドキューに追加します。

ページビューから収集されたデータは有用ですが、それだけでは全体像を把握できません。ユーザーがサイトやアプリをどのように利用しているかをより正確に把握するには、追加のインタラクション データを Google アナリティクスに送信する必要があります。

Google アナリティクスでは、ページビューイベントソーシャル インタラクション例外ユーザー タイミングなど、さまざまな種類のインタラクション データがサポートされています。カスタム速度のデータを Google アナリティクスに送信するには、次のコマンド シグネチャを使用します。

ga('send', 'timing', timingCategory, timingVar, timingValue);

ここで、timingCategory はタイミング ヒットを論理グループに整理するための文字列、timingVar は測定する変数、timingValue は実際の所要時間(ミリ秒単位)です。

この仕組みを実際に確認するには、measureCssUnblockTime 関数の console.log ステートメントを次のように更新します。

ga('send', 'timing', 'CSS', 'unblock', measureDuration('css:unblock'));

上記のコードは状況によっては機能しますが、注意すべき重要な点が 2 つあります。

  • 前の手順では、ブラウザが User Timings API をサポートしている場合にのみ measureDuration 関数が実行されるように更新しました。つまり、場合によっては undefined が返されます。未定義のデータを Google アナリティクスに送信する理由はないため(場合によってはレポートが混乱する可能性もあります)、measureDuration が値を返す場合にのみ、このタイミング ヒットを送信する必要があります。
  • measureDuration が値を返す場合、それは DOMHighResTimeStamp であり、ミリ秒を超える精度になります。Google アナリティクスの timingValue は整数である必要があるため、measureDuration から返された値を丸める必要があります。

これらの落とし穴を考慮して、measureDuration 関数の return 文を更新し、戻り値を丸めます。

function measureDuration(mark, opt_reference) {
  if (window.__perf) {
    // ...
    return Math.round(measure.duration);
  }
}

また、問題の指標に値が存在する場合にのみ実行されるように、タイミング コマンドを更新します。たとえば、measureCssUnblockTime 関数は次のように更新されます。

function measureCssUnblockTime() {
  var cssUnblockTime = measureDuration('css:unblock');
  if (cssUnblockTime) {
    ga('send', 'timing', 'CSS', 'unblock', cssUnblockTime);
  }
}

他のすべての測定関数についても同様の更新を行う必要があります。完了すると、最終的な perf-analytics.js ファイルは次のようになります。

window.onload = function() {
  measureCssUnblockTime();
  measureWebfontPerfAndFailures();
  measureImagesVisibleTime();
  measureJavaSciptExecutionTime();
};


/**
 * Calculates the time duration between the responseEnd timing event and when
 * the CSS stops blocking rendering, then sends this measurement to Google
 * Analytics via a timing hit.
 */
function measureCssUnblockTime() {
  var cssUnblockTime = measureDuration('css:unblock');
  if (cssUnblockTime) {
    ga('send', 'timing', 'CSS', 'unblock', cssUnblockTime);
  }
}


/**
 * Calculates the time duration between the responseEnd timing event and when
 * the web fonts are downloaded and active, then sends this measurement to
 * Google Analytics via a timing hit. If an error occurs loading the font, an
 * error event is sent to Google Analytics.
 */
function measureWebfontPerfAndFailures() {
  if (window.Promise) {
    new Promise(function(resolve, reject) {
      var loaded = /wf-(in)?active/.exec(document.documentElement.className);
      var success = loaded && !loaded[1]; // No "in" in the capture group.
      if (loaded) {
        success ? resolve() : reject();
      }
      else {
        var originalAciveCallback = WebFontConfig.active;
        WebFontConfig.inactive = reject;
        WebFontConfig.active = function() {
          originalAciveCallback();
          resolve();
        };
        // In case the webfont.js script failed to load.
        setTimeout(reject, WebFontConfig.timeout);
      }
    })
    .then(function() {
      var fontsActiveTime = measureDuration('fonts:active');
      if (fontsActiveTime) {
        ga('send', 'timing', 'Fonts', 'active', fontsActiveTime);
      }
    })
    .catch(function() {
      ga('send', 'event', 'Fonts', 'error');
    });
  }
}


/**
 * Calculates the time duration between the responseEnd timing event and when
 * all images are loaded and visible on the page, then sends this measurement
 * to Google Analytics via a timing hit.
 */
function measureImagesVisibleTime() {
  var imgVisibleTime = measureDuration('img:visible');
  if (imgVisibleTime) {
    ga('send', 'timing', 'Images', 'visible', imgVisibleTime);
  }
}


/**
 * Calculates the time duration between the responseEnd timing event and when
 * all synchronous JavaScript files are downloaded and executed, then sends
 * this measurement to Google Analytics via a timing hit.
 */
function measureJavaSciptExecutionTime() {
  var jsExecuteTime = measureDuration('js:execute');
  if (jsExecuteTime) {
    ga('send', 'timing', 'JavaScript', 'execute', jsExecuteTime);
  }
}


/**
 * Accepts a mark name and an optional reference point in the navigation timing
 * API and returns the time duration between the reference point and the last
 * mark (chronologically). The return value is rounded to the nearest whole
 * number to be compatible with Google Analytics.
 * @param {string} mark The mark name.
 * @param {string=} opt_reference An optional reference point from the
 *     navigation timing API. Defaults to 'responseEnd'.
 * @return {?number} The time duration as an integer or undefined if no
 *     matching marks can be found.
 */
function measureDuration(mark, opt_reference) {
  if (window.__perf) {
    var reference = opt_reference || 'responseEnd';
    var name = reference + ':' + mark;

    // Clears any existing measurements with the same name.
    performance.clearMeasures(name);

    // Creates a new measurement from the reference point to the specified mark.
    // If more than one mark with this name exists, the most recent one is used.
    performance.measure(name, reference, mark);

    // Gets the value of the measurement just created.
    var measure = performance.getEntriesByName(name)[0];

    // Returns the measure duration.
    return Math.round(measure.duration);
  }
}

最終的な index.html ファイルは次のようになります。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Performance Analytics Demo</title>

  <!-- Start navigation timing feature detect -->
  <script>window.__perf = window.performance && performance.mark;</script>
  <!-- End navigation timing feature detect -->

  <!-- Start fonts -->
  <script>
  window.WebFontConfig = {
    google: {families: ['Roboto:400,700,400italic']},
    timeout: 10000,
    active: function() {
      __perf && performance.mark('fonts:active');
    }
  };
  </script>
  <script async src="https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js"></script>
  <!-- End fonts -->

  <!-- Start CSS -->
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
  <style>
    body { font-family: Roboto, sans-serif; margin: 1em; }
    img { float: left; height: auto; width: 33.33%; }
    .gallery { overflow: hidden; }
  </style>
  <script>__perf && performance.mark('css:unblock');</script>
  <!-- End CSS -->

</head>
<body>

  <div class="container">

    <!-- Start images -->
    <div class="gallery">
      <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
      <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
      <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
    </div>
    <script>__perf && performance.mark('img:visible')</script>
    <!-- End images -->

    <h1>Performance Analytics Demo</h1>
    <p>Real performance data from real users.</p>

  </div>

  <!-- Start JavaScript -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
  <script>__perf && performance.mark('js:execute');</script>
  <!-- End JavaScript -->

  <!-- Start analytics tracking snippet -->
  <script>
  window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
  ga('create', 'UA-XXXXX-Y', 'auto');
  ga('send', 'pageview');
  </script>
  <script async src="https://www.google-analytics.com/analytics.js"></script>
  <!-- End analytics tracking snippet -->

  <!-- Start performance analytics -->
  <script async src="perf-analytics.js"></script>
  <!-- End performance analytics -->

</body>
</html>

このページを読み込んでネットワーク パネルのリクエストを確認すると、次のようなものが表示されます。

Screen Shot 2016-05-10 at 6.57.23 PM.png

これは便利ですが、このデータを URL エンコードされたリクエストとして確認するのは面倒な場合があります。また、何らかの理由でこれらのリクエストが表示されない場合、障害が発生した場所を特定することは非常に困難です。

ローカルで開発する場合は、analytics.js のデバッグ バージョンを使用することをおすすめします。このバージョンでは、analytics.js の各コマンドが実行されるたびに、デバッグに役立つ情報がコンソールに記録されます。Android の新しいソフトウェア バージョンを

index.html の analytics.js URL を analytics_debug.js に更新してブラウザ コンソールを開くと、次のようなステートメントが出力されます。

Screen Shot 2016-05-10 at 6.54.13 PM.png

このデモページのパフォーマンス測定を実装する方法を理解したら、ご自身のサイトに追加して、実際のユーザーデータを Google アナリティクスに送信してみてください。

収集したデータに関するレポート

パフォーマンス データの収集を数日間行うと、そのデータをレポートして、実際のユーザーに対してサイトとそのリソースがどれくらいの速さで読み込まれているかについての実用的な分析情報を得ることができます。

Google アナリティクスのユーザー タイミング レポートにアクセスするには、上部の [レポート] タブをクリックし、サイドバー ナビゲーションから [行動] > [サイトの速度] > [ユーザー タイミング] を選択します(または、ヘルプセンターのユーザー タイミング レポートを表示する手順に沿って操作します)。

Google アナリティクスでユーザー タイミング レポートを読み込むと、送信したデータに対応するタイミング カテゴリが表示されます。いずれかをクリックすると、タイミング データの詳細な可視化が表示されます。次の画像は、過去 24 時間にわたって Google Fonts を使用した実際のウェブサイトでのフォントの読み込み時間の例です。

Screen Shot 2016-05-10 at 7.16.07 PM.png

これで、この Codelab は終了です。さらに詳しく知りたい場合は、次のセクションで、このコードを基盤としてさらに多くの分析情報を得るための方法をいくつかご紹介します。

このコードラボで取り上げるパフォーマンス指標は、実際のユーザーに対するサイトの読み込みを測定するうえで非常に重要ですが、これはほんの始まりにすぎません。パフォーマンス分析をさらに詳しく行うには、より多くの指標をトラッキングするのが簡単な方法です。

このコードラボでは、リソースがユーザーに提供されたタイミングに関連する指標を追跡しました。必要に応じて、これらのほとんどをさらに細かく分類することもできます。たとえば、JavaScript の実行が終了したタイミングを測定するだけでなく、読み込みが開始されたタイミング、読み込みが終了したタイミング、実行が開始されたタイミング、そして最終的に実行が終了したタイミングを測定できます。これらの指標はそれぞれ、単独ではわからない問題を明らかにすることができます。

より詳細な分析を行うだけでなく、一般的なパフォーマンス分析戦略についても包括的に検討する必要があります。目標は何ですか?成功とは何か

分析を行う場合は、通常、まず何らかの質問から始め、その質問に答えるためにデータをどのように使用するかを考えます。

たとえば、次の質問のリストと、このコードラボで得た知識を使用して分析を行い、これらの質問に答える方法について考えてみましょう。

  • トラッキングしている指標の値は、時間の経過とともに減少していますか、増加していますか?
  • サービス ワーカーやローカル ストレージによるオフライン キャッシュの使用は、サイトの全体的なパフォーマンスにどのような影響を与えますか?
  • リソースは最適に読み込まれていますか?つまり、リソースがダウンロードされてから使用可能になるまでに大きなギャップがあるかどうかです。
  • パフォーマンスと、トラッキングしている他の指標(登録率、サイト滞在時間、購入など)との間に相関関係はありますか?

最後に、ウェブ パフォーマンスや Google アナリティクスについて詳しくは、以下のリソースをご覧ください。