使用 Google Analytics (分析) 評估重要成效指標

在本程式碼研究室中,您將瞭解如何使用 Google Analytics (分析)User Timings API 來評估網站或應用程式的實際成效,並針對使用者體驗進行最佳化以提升使用者體驗。

WebPagetest.org」這類工具是提升廣告成效的入門依據,但實際上對網站效能的真正測試將永遠有實際的使用者資料。

如果您是網站經營者,很可能就是透過 Google Analytics (分析) 評估流量以及裝置和瀏覽器使用情形等數據。只要稍微增加程式碼,就能加入成效指標。

課程內容

  • 如何使用 User Timings API 準確有效地評估成效指標
  • 如何將這項資料傳送給 Google Analytics (分析),以便納入報表

軟硬體需求

您要如何使用本教學課程?

唯讀閱讀 閱讀內容並完成練習

您對於建立網站或應用程式的體驗有什麼評價?

初級 中級 專業

您可以將所有程式碼範例下載到電腦...

下載 Zip

...或是透過指令列複製 GitHub 存放區。

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

此程式碼範例會細分成子目錄,對應這個程式碼研究室中每個編號步驟。方便您在程式碼研究室中輕鬆略過,或確認導入是否正確。

如果你具備差異比較程式的存取權,就可以查看該計畫從各步驟到實際變更的內容。

在本程式碼研究室中,您將使用一個能夠載入下列資產的 HTML 檔案:

  • 網路字型
  • 樣式表
  • 映像檔
  • JavaScript

而您要編寫的新程式碼來評估這些素材資源類型的主要成效指標。

素材資源成效注意事項

如果您讀過成效最佳化方面的知識,或許您已經知道,上述各個資產類型都有各自的優點,而且會影響整體感知的成效。

CSS

例如,樣式表會封鎖 DOM 之後的所有元素顯示結果,這表示瀏覽器必須要求、下載及剖析樣式表,然後才算轉譯此 DOM 之後的所有內容。因此,最好將樣式表放在文件的 [head] 部分中。此外,由於 CSS 會封鎖內容,一般建議不要僅將重要的 CSS 置於 <head</g> 中,之後再以非同步方式載入非關鍵 CSS。

JavaScript

相反地,JavaScript 並不會封鎖轉譯,但會阻止 DOM 剖析與建構。這是必要的,因為 JavaScript 可以修改 DOM;也就是說,當瀏覽器看到 <script> 標記 (不含非同步指令碼) 時,就必須執行程式碼,才能繼續使用下一個標記。如果 <script> 標記參照外部 JavaScript 檔案,則必須先下載並執行程式碼,才能繼續操作。

基於這個原因,我們通常會建議在 JavaScript 結尾標記「</body>」標記之前載入 JavaScript,這樣大部分的 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>

接著,開啟 Chrome 的 Web Server,並在剛建立的目錄中啟動本機伺服器。請務必勾選 [自動顯示 index.html]。

2016 年 5 月 11 日下午 1.03.43 的螢幕擷取畫面

您現在應該可以在瀏覽器中前往 http://127.0.0.1:8887/ 並查看示範檔案。如下所示:

2016 年 5 月 11 日上午 10.59.03 上午.

示範頁面執行後,請花點時間查看程式碼,並查看載入的各種資產類型。在接下來的幾個步驟中,您將加入程式碼來評估這些素材資源的載入時機,並與使用者進行互動。

如先前在素材資源成效注意事項一節所述,CSS 會禁止顯示 DOM 元素,以及指令碼在 DOM 之後執行的指令碼。

您剛剛建立的示範檔案包含下列 CSS,這些 CSS 參照了 Bootstrap 和一些內嵌樣式。

<!-- 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 元素轉譯和指令碼執行,因此您可以在儲存目前時間的 CSS 後方加上 <script> 標記,以決定 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 方法使用,用來計算兩個標記之間的時差。除了您標記的內容以外,您也可以使用瀏覽器在 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 -->

完成這個步驟後,程式碼應該與程式碼研究室存放區中 01-css 目錄的內容相符。

如果在瀏覽器中載入網頁並開啟開發人員控制台,系統應該會顯示以下輸出內容:

Screen Shot 2016-05-17 上午 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><link> 標記,它似乎似乎可以為判斷是否在 <script> 之後在 <br class="ph-2- 1}

不巧,這並不簡單。

樣式表是用來封鎖 JavaScript 的執行,因為樣式表內容是用於建構 CSSOM。由於載入的 JavaScript 必須存取 CSSOM,因此執行程序可能會延遲,直到 CSSOM 完全建構完成為止。

擷取是瀏覽器不需建構字型就能建構 CSSOM。也就是說,如果您在字型標籤 <link> 標記後面立即透過內嵌指令碼標記將標記加入 DOM,但是標記可能是在字型完全載入「之前」才會發生。

瀏覽器載入字型前,必須先使用 JavaScript 才能判斷字型是否有效,並且可供使用在網頁上。幸好,透過 JavaScript 載入字型也有助於提升成效,因為它不需要對 CSS 檔案執行額外的封鎖要求。

大部分網路字型 (包括 Google 字型、Typekit 和 font.com 字型) 都可以透過 webfont.js 指令碼載入,而這是一種由 Google 和 Typekit 共同開發的指令碼。

若要更新主要文件以使用 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 -->

關於上述程式碼,有兩點需要注意:

  • 會在目前的回呼中建立「fonts:active"」標記,以便日後測量字型載入所需的時間。
  • 會載入 webfonts.js 的 <script> 標記包含 async 屬性,因此該文件不會禁止剖析或轉譯文件的其餘部分 (<link> 標記則無效)。

上述程式碼確實會建立「fonts:active」標記,但測量這個標記並記錄至控制台並不像「css:unblock」標記一樣簡單。這是因為現在字型會以非同步方式進行,因此如果嘗試測量「font」的「fonts:active&quot」標記,就像在「window.onload」處理常式中一樣 (也就是與「css:unblock"」相同),該字型很可能尚未載入。

如要解決這個問題,您可以建立承諾,在字型載入後就予以解決。下列函式可以為您完成這項工作。複製該檔案並貼到 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();
};

完成這個步驟後,程式碼應該與程式碼研究室存放區中 02-fonts 目錄的內容相符。

如果在瀏覽器中載入網頁並開啟開發人員控制台,系統應該會顯示以下輸出內容:

Screen Shot 2016-05-17 上午 11.13.22 AM.png

瞭解圖片的顯示效果並不容易。如先前步驟所示,樣式表和同步 <script> 標記可能會封鎖轉譯、剖析及指令碼執行。您可能不知道,這兩款瀏覽器都無法封鎖瀏覽器的預先載入掃描器。

預載掃描器就是所有新式瀏覽器採用的一種,能夠提升網站效能,即使在不具大量封鎖功能的資產網站上無法執行效能仍能達到目標。因此某些素材資源雖然可能會封鎖剖析、轉譯或指令碼執行,但不一定要封鎖下載內容。因此,瀏覽器會在開始建構 DOM 之前掃描 HTML 檔案,並尋找可以立即開始下載的資產。

就圖片而言,這意味著您的圖片可能已在 DOM 中新增時,可能已下載完成。這也表示,在下載圖片時,不一定能擁有良好的成效指標。您應該重視的效能指標是使用者看到圖片的時間點。

將圖片加入 DOM 之前,下載圖片時,其顯示位置是指在 DOM 中的圖片位置。另一方面,如果圖片尚未加入 DOM 而未下載,代表其可看見的時間點是在其 onload 處理常式啟動時觸發。

因此,如要瞭解圖片的顯示時機,您必須同時處理兩種情況。

方法是在每一個連結的 onload 處理常式及內嵌 &script 標記中加入標記 (在 DOM 的最後一個圖片後方加入標記)。建議最後一個標示是代表所有圖片可見的標記。

如要在載入圖片及轉譯圖片時新增標記,請更新 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();
};

完成這個步驟後,程式碼應該與程式碼研究室存放區中 03-images 目錄的內容相符。

如果在瀏覽器中載入網頁並開啟開發人員控制台,系統應該會顯示以下輸出內容:

Screen Shot 2016-05-17 上午 11.13.39 AM.png

由於沒有 async 屬性的 <script> 標記會封鎖 DOM 剖析作業,因此在下載及執行之前,您可以確定所有指令碼已經完成的執行點,於 DOM 的最後一個同步 <script> 之後加上內嵌指令碼標記。

請注意,在這種情況下,使用 onload 處理常式會無法運作,因為瀏覽器必須在載入指令碼後執行指令碼,而且也需要一些時間。可快速載入但執行速度緩慢的指令碼和速度較慢的指令碼一樣不好。

如要追蹤在主要文件中載入和執行所有 JavaScript 的時間,請更新下列程式碼中的 index.html 部分:

<!-- 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 -->

這將會新增名為「js:execute」的標記。在 jQuery 和 Bootstrap's 外掛程式的指令碼下載和執行完成之後,

如要測量所需時間,請將下列函式新增至 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();
};

完成這個步驟後,程式碼應該與程式碼研究室存放區中 04-javascript 目錄的內容相符。

如果在瀏覽器中載入網頁並開啟開發人員控制台,系統應該會顯示以下輸出內容:

2016 年 5 月 17 日上午 11.14.03 上午.png

並非所有瀏覽器都支援 JavaScript 承諾使用功能或 User Timing API。此外,如果您在執行以上程式碼的情況下在瀏覽器中執行程式碼,而不具備以上任一功能,系統就會顯示錯誤。

如要解決這個問題,您可以使用功能偵測。請在後面加入字型區段。這行 JavaScript 會偵測支援 performance.mark 方法,因此您必須在該方法使用前加入網頁:

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

接著,在 index.html 中呼叫 performance.mark 的任何位置,在前面加上功能偵測。以下範例是更新圖片區塊程式碼的範例,但請務必一併更新其他部分。

<!-- 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 -->

現在,「__perf」變數已設定為「window」,但您也可以在 perf-analytics.js 中使用這個變數,確保您不會呼叫目前瀏覽器不支援的方法。

必須更新 measureDuration 函式,才能新增下列條件:

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

最後,由於並非所有瀏覽器都支援 JavaScript 承諾,measureWebfontPerfAndFailures 函式也必須納入特定條件中:

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

現在,您應該能夠順利地在任何瀏覽器中執行程式碼。

完成這個步驟後,程式碼應該與程式碼研究室存放區中 05-feature-detects 目錄的內容相符。

這個程式碼研究室的最後一個步驟是將記錄到主控台的資料,改成傳送給 Google Analytics (分析)。此外,您必須先在網頁中加入 analytics.js 程式庫預設追蹤程式碼片段,才能將資料傳送到 Google Analytics (分析)。

在主要 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 Analytics (分析) 加入網站,則您需要將「UA-XXXXX-Y」換成預留位置,並以您在 Google Analytics (分析) 建立新資源時取得的追蹤 ID 取代。

analytics.js 追蹤程式碼片段可執行以下四個主要操作:

雖然只從瀏覽量收集的資料很實用,但不代表能述說完整的故事。為了讓使用者更瞭解自己的網站或應用程式體驗,您必須傳送額外的互動資料給 Google Analytics (分析)。

Google Analytics (分析) 支援多種互動資料,分別是瀏覽量事件社交互動例外狀況和 (最後但非最終的使用者載入時間)。您可以利用下列指令簽名來傳送使用者載入時間資料至 Google Analytics (分析):

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

其中 timingCategory 是一個字串,可讓您將時間命中整理成邏輯群組,timingVar 則是您要評估的變數,timingValue 則是實際時間長度 (以毫秒為單位)。

如要瞭解其運作方式,請使用以下方式更新 measureCssUnblockTime 函式中的 console.log 陳述式:

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

上述程式碼在某些情況下適用,但請注意以下兩件事:

  • 在上一個步驟中,只有瀏覽器支援 User Timings API 時,才需更新 measureDuration 函式。也就是說,有時會傳回 undefined。由於沒有將任何資料傳送到 Google Analytics (分析) 的原因 (在某些情況下,甚至可能使報表不堪斷言),因此只有在 measureDuration 傳回值時,才應傳送這個時間匹配。
  • measureDuration 傳回值時,它是 DOMHighResTimeStamp,精確度可大於一毫秒。由於 Google Analytics (分析) 中的 timingValue 必須是整數,因此必須是 measureDuration 傳回的值。

為了反映上述錯誤,請更新 measurementDuration 函式中的傳回陳述式,以四捨五入至傳回值:

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

這很實用,但是將這些資料視為網址編碼的要求相當麻煩。此外,如果您基於任何理由看不到這些要求,我們很難追蹤失敗發生的位置。

在本機開發服務時,比較好的做法是使用 analytics.js 偵錯版本。這項工具會在您每次執行 analytics.js 指令時,將實用的偵錯資訊記錄到主控台中。如果您

index.html 中的 analytics.js 網址更新為 analytics_debug.js 並開啟瀏覽器控制台,這樣您就會看到像這樣的聲明:

2016-05-10 下午 6.54.13 PM.png

您已經瞭解如何為這個示範頁面導入成效評估,不妨立即將這份資料加入自己的網站,並將實際的使用者資料傳送至 Google Analytics (分析)。

針對所收集的資料製作報表

收集成效資料幾天後,您就能根據這些資料製作可行的深入分析資料,藉此瞭解自家網站及其資源實際載入的速度,藉此瞭解使用者實際載入的速度。

如要前往 Google Analytics (分析) 的「使用者載入時間」報表,請按一下頂端的「報表」分頁,然後從側欄導覽選單選取 [行為] > [網站速度] > [使用者載入時間],或是依照指示前往說明中心查看「使用者載入時間」報表。

在 Google Analytics (分析) 中載入「使用者載入時間」報表後,您應該會看到與您所傳送資料相對應的時間類別。點選任一指標,即可查看時間資料的詳細圖表。以下為過去 24 小時內,使用 Google Fonts 實際網站上的字型載入時間範例。

2016-05-10 晚上 7.16.07 PM.png

恭喜!你已成功完成這個程式碼研究室。如果還想深入瞭解,請參閱下一節提供的建議,瞭解如何以這個程式碼為基礎來深入瞭解。

本程式碼研究室所介紹的成效指標,對於評估實際使用者的網站載入成效十分重要,但是這只是個開端。如果想要深入瞭解成效分析,最簡單的方法就是追蹤更多的指標。

在這個程式碼研究室中,您追蹤了使用者可用資源的相關指標。如有需要,您還可以進一步細分其中的內容。舉例來說,您不必測量 JavaScript 的執行時間,而是開始評估開始載入、載入完成、開始執行的時間,以及最終執行完畢的時間。這些指標都可能會找出其中一項指標無法呈現的問題。

除了更精細的分析,您也應該更全面地分析自己的整體效能分析策略。目標是什麼?成功的是什麼?

面對任何類型的分析,您通常會先從某種問題開始,然後研究如何使用資料來回答問題。

舉例來說,您不妨參考以下問題清單,並瞭解如何運用本程式碼研究室所得到的知識,運用數據分析找出這些問題的答案:

  • 您追蹤的指標值是否隨著時間而減少或增加?
  • 使用服務業者或本機儲存空間的離線快取對網站的整體效能有何影響?
  • 您的資源是否已正確載入?例如,在下載資源和其資源的可用時間方面,是否有明顯落差?
  • 您所追蹤的成效和其他追蹤指標 (例如註冊率、網站停留時間、購買次數等) 之間是否有任何關聯?

最後,如果您想要進一步瞭解網路效能或 Google Analytics (分析),請參考下列實用資源: