Đo lường các chỉ số hiệu suất quan trọng bằng Google Analytics

Trong lớp học lập trình này, bạn sẽ tìm hiểu cách sử dụng Google AnalyticsAPI Thời gian người dùng để đo lường hiệu suất thực tế của trang web hoặc ứng dụng và tối ưu hóa trang web để cải thiện trải nghiệm cho người dùng.

Các công cụ như WebPagetest.org là một khởi đầu tuyệt vời để tối ưu hóa hiệu suất, nhưng thử nghiệm thực tế về hiệu suất của trang web sẽ luôn là dữ liệu thực tế của người dùng thực.

Nếu bạn điều hành một trang web, có nhiều khả năng bạn đã sử dụng Google Analytics để đo lường lưu lượng truy cập cũng như những nội dung như thiết bị và việc sử dụng trình duyệt. Chỉ với một chút mã bổ sung, bạn có thể thêm các chỉ số hiệu suất vào danh sách kết hợp.

Kiến thức bạn sẽ học được

  • Cách đo lường chỉ số hiệu suất chính xác và hiệu quả bằng API Thời gian người dùng
  • Cách gửi dữ liệu đó đến Google Analytics để có thể kết hợp vào báo cáo của bạn

Bạn cần có

  • Một trình duyệt có bảng điều khiển dành cho nhà phát triển
  • Máy chủ web cho Chrome hoặc sử dụng máy chủ web bạn chọn
  • Mã mẫu
  • Trình chỉnh sửa văn bản
  • (Không bắt buộc) tài khoản Google Analytics

Bạn sẽ sử dụng hướng dẫn này như thế nào?

Chỉ đọc qua Đọc qua và hoàn thành các bài tập

Vui lòng cho biết trải nghiệm của bạn khi xây dựng các trang web hoặc ứng dụng.

Công ty Trung cấp Đặc biệt

Bạn có thể tải tất cả mã mẫu xuống máy tính...

Tải tệp zip xuống

...hoặc sao chép kho lưu trữ GitHub từ dòng lệnh.

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

Mã mẫu được chia thành các thư mục con tương ứng với mỗi bước được đánh số trong phòng thí nghiệm mã này. Bạn có thể sử dụng mã này để dễ dàng bỏ qua trong phòng thí nghiệm mã hoặc xác minh rằng bạn đã triển khai đúng cách.

Nếu bạn có quyền truy cập vào một chương trình khác nhau, bạn có thể sử dụng chương trình đó để xem chính xác những gì đã thay đổi theo từng bước.

Trong phòng thí nghiệm mã này, bạn sẽ lấy một tệp HTML duy nhất tải các nội dung sau:

  • Phông chữ trên web
  • Biểu định kiểu
  • Hình ảnh
  • JavaScript

Ngoài ra, bạn sẽ viết mã mới đo lường các chỉ số hiệu suất chính cho từng loại nội dung trong số này.

Những điều cần lưu ý về hiệu suất tài sản

Nếu bạn đã từng đọc bất cứ điều gì về tối ưu hóa hiệu suất, có thể bạn đã biết rằng từng loại thành phần này có các điều kiện khác nhau và có thể ảnh hưởng đến hiệu suất nhận dạng tổng thể theo nhiều cách.

Dịch vụ so sánh giá (CSS)

Ví dụ: biểu định kiểu chặn hiển thị tất cả các thành phần trong DOM sau chúng, có nghĩa là trình duyệt phải thực hiện một yêu cầu cho biểu định kiểu, tải xuống và phân tích cú pháp nó trước khi có thể hiển thị bất kỳ nội dung nào trong DOM xuất hiện sau đó. Vì lý do này, thông thường, tốt nhất là bạn nên đặt biểu định kiểu trong <head> của tài liệu. Và do tính chất chặn của CSS, CSS thường chỉ nên đặt CSS quan trọng của bạn trong <head> và tải CSS không quan trọng không đồng bộ sau đó.

JavaScript

Trái lại, JavaScript không chặn hiển thị nhưng sẽ chặn phân tích cú pháp và xây dựng DOM. Điều này cần thiết vì JavaScript có thể sửa đổi DOM, nghĩa là bất cứ khi nào trình duyệt thấy thẻ <script> (không bao gồm các tập lệnh không đồng bộ), trình duyệt phải thực thi mã trước khi tiếp tục chuyển đến thẻ tiếp theo. Nếu thẻ <script> tham chiếu đến một tệp JavaScript bên ngoài, thì thẻ đó phải tải xuống và thực thi mã trước khi tiếp tục.

Vì lý do này, JavaScript thường được tải ngay trước thẻ đóng & lt;/body> để phần lớn DOM có sẵn nhanh nhất có thể.

Phông chữ trên web

Nếu tải phông chữ trên web, bạn cũng có thể chọn chặn hiển thị tài liệu cho đến khi sử dụng phông chữ. Trong trường hợp này, điều quan trọng là phải hiểu được khoảng thời gian này thực sự mất bao lâu cho người dùng của bạn. Phông chữ web có thể tải nhanh cho bạn nhưng rất chậm cho phần lớn người truy cập trang web của bạn. Đó là lý do việc đo lường và đưa ra quyết định về dữ liệu thực tế rất quan trọng.

Hình ảnh

Cuối cùng, hình ảnh thực sự có thể làm cho một trang web tồn tại, nhưng chúng cũng có thể mất nhiều thời gian nhất để tải. Hiểu được ý nghĩa của việc này và có thể phát hiện bất kỳ mối tương quan nào giữa kiểu sử dụng và thời gian tải trang là yếu tố rất quan trọng để hiểu cách tối ưu hoá.

Bước đầu tiên trong phòng thí nghiệm mã này là xem trang minh họa trông như thế nào trước khi thêm mã đo lường hiệu suất.

Để xem bản minh họa, hãy tạo một thư mục mới và thêm một tệp bên trong đó có tên là index.html. Sau đó, sao chép và dán mã bên dưới vào tệp 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>

Tiếp theo, hãy mở Máy chủ web dành cho Chrome và khởi động máy chủ cục bộ trong thư mục bạn vừa tạo. Hãy nhớ chọn "tự động hiển thị index.html" đã đánh dấu.

Ảnh chụp màn hình lúc 1 giờ 35 phút chiều, ngày 5 tháng 5 năm 2016

Bây giờ bạn có thể điều hướng đến http://127.0.0.1:8887/ trong trình duyệt và xem tệp minh họa. Hàm này có dạng như sau.

Ảnh chụp màn hình lúc 10:59 sáng ngày 11 tháng 5 năm 2016.png

Sau khi bạn có trang minh họa đang chạy, hãy dành chút thời gian để xem mã và xem tất cả các loại tài sản đang được tải. Trong vài bước tiếp theo, bạn sẽ thêm mã để đo lường khi những tài sản này được tải và người dùng có thể tương tác.

Như đã đề cập trong phần xem xét hiệu suất nội dung trước đó, CSS sẽ chặn hiển thị các phần tử DOM cũng như thực thi các tập lệnh đứng sau DOM trong DOM.

Tệp minh họa bạn vừa tạo chứa CSS sau, tham chiếu Bootstrap và một vài kiểu cùng dòng.

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

Vì CSS chặn cả quá trình kết xuất các phần tử DOM và thực thi tập lệnh, nên hệ thống có thể xác định thời điểm CSS hoàn tất việc chặn bằng cách thêm thẻ <script> ngay sau CSS lưu trữ thời gian hiện tại.

Bạn có thể làm việc đó bằng cách tạo một biến và chỉ định new Date() cho biến đó, nhưng nhờ API Thời gian người dùng, có một cách dễ dàng hơn nhiều: phương thức performance.mark.

Để đánh dấu khi CSS đã chặn xong cả hoạt động kết xuất và thực thi tập lệnh, hãy thêm dòng mã sau ngay trước nhận xét <!-- End CSS --> đã đóng.

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

Phương thức performance.mark tạo dấu thời gian có độ phân giải cao tại thời điểm chính xác này và liên kết dấu thời gian với bất kỳ tên nào được chuyển vào phương thức. Trong trường hợp này, bạn đã đặt tên nhãn hiệu "css:unblock"

Phương thức performance.mark đi kèm với phương thức performance.measure. Phương pháp này dùng để tính toán sự khác biệt về thời gian giữa hai điểm (ngoài các dấu bạn tạo, bạn cũng có thể sử dụng các dấu mà trình duyệt tự động tạo cho các điểm khác nhau trong API Thời gian điều hướng).

Hàm tiện ích sau đây đo lường và trả về khoảng thời gian giữa một mốc bạn đã thêm và dấu ResponseEnd do API Thời gian điều hướng tạo.

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;
}

Để bắt đầu sử dụng hàm tiện ích này, hãy tạo một tệp mới có tên perf-analytics.js (trong cùng thư mục với tệp index.html), sau đó sao chép và dán mã ở trên vào tệp đó.

Bây giờ, hàm này đã được xác định, bạn có thể gọi hàm đó và chuyển trong "css:unblock" tên dấu. Để không làm ảnh hưởng đến bất kỳ quá trình tải tài nguyên nào khác, bạn nên trì hoãn việc chạy các phép đo này cho đến khi sự kiện tải trong cửa sổ kích hoạt.

Khi bạn đã viết một hàm để gọi mã này, tệp perf-analytics.js của bạn sẽ trông giống như sau:

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;
}

Cuối cùng, bạn sẽ cần tải tập lệnh perf-analytics.js từ index.html. Để thực hiện việc này, hãy thêm thẻ tập lệnh sau vào tài liệu chính của bạn. Hãy nhớ thêm hạn mức này vào lần cuối để không ảnh hưởng đến việc tải các tài nguyên khác.

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

Sau khi bạn đã hoàn tất bước này, mã của bạn phải khớp với nội dung trong thư mục 01-css của kho lưu trữ phòng thí nghiệm mã.

Nếu bạn tải trang trong một trình duyệt và mở bảng điều khiển dành cho nhà phát triển, bạn sẽ thấy một kết quả như sau:

Ảnh chụp màn hình lúc 11/15/2016 – 17/5/2016

Phông chữ web thường được tải thông qua biểu định kiểu bên ngoài, như thấy trong tệp minh họa ban đầu:

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

Vì đây là thẻ <link> vào tệp CSS, nên có vẻ như bạn cần xác định phông chữ đã được tải và sẵn sàng sử dụng đơn giản như thêm một dấu bên trong thẻ <script> ngay sau <link>, giống như trong bước 1.

Không may là việc này không đơn giản như thế này.

Biểu định kiểu chặn quá trình thực thi JavaScript vì nội dung của biểu định kiểu được dùng để tạo CSSOM và vì JavaScript có thể cần tải để truy cập vào CSSOM, nên quá trình thực thi phải bị trì hoãn cho đến khi CSSOM được xây dựng hoàn chỉnh.

Lưu ý là trình duyệt có thể tạo CSSOM mà không thực sự tải phông chữ xuống, có nghĩa là nếu bạn thêm một dấu thông qua thẻ tập lệnh cùng dòng vào DOM ngay sau khi biểu định kiểu & phông chữ <link> thẻ, thì cơ hội đánh dấu là sẽ xảy ra trước khi phông chữ được tải đầy đủ.

Trước khi các sự kiện tải phông chữ xuất hiện trong trình duyệt, chúng tôi cần JavaScript để xác định thời điểm một phông chữ thực sự hoạt động và sẵn sàng để sử dụng trên trang. Rất may là việc tải phông chữ qua JavaScript cũng mang lại lợi ích về hiệu suất, vì nó không yêu cầu thêm yêu cầu chặn đối với tệp CSS.

Bạn có thể tải hầu hết các phông chữ trên web (bao gồm phông chữ Google, typekit và phông chữ font.com) thông qua tập lệnh webfont.js do Google và Typekit đồng phát triển.

Để cập nhật tài liệu chính sử dụng webfont.js để tải phông chữ (thay vì thẻ <link> thẻ), hãy thay thế phần phông chữ của mã bằng đoạn mã sau:

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

Có hai điều quan trọng cần lưu ý về mã ở trên:

  • Công cụ này tạo ra một "fonts:active" đánh dấu trong lệnh gọi lại đang hoạt động, để bạn có thể đo lường thời gian tải phông chữ sau đó.
  • Thẻ <script> tải webfonts.js chứa thuộc tính async, vì vậy, thẻ này sẽ không chặn việc phân tích cú pháp hoặc hiển thị phần còn lại của tài liệu (không đúng với thẻ <link>).

Mặc dù những mã ở trên tạo dấu "fonts:active" nhưng việc đánh dấu dấu này và ghi vào bảng điều khiển không đơn giản như đánh dấu "css:unblock" Lý do là khi tải phông chữ giờ đây xảy ra không đồng bộ, vì vậy, nếu bạn cố gắng đo "fonts:active" Mark trong trình xử lý window.onload (như bạn đã làm với "css:unblock"), thì phông chữ rất có thể sẽ chưa được tải.

Để giải quyết vấn đề này, bạn có thể tạo lời hứa đã được giải quyết sau khi phông chữ được tải. Hàm sau đây sẽ thực hiện việc này cho bạn. Sao chép và dán vào tệp 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')
  });
}

Ngoài ra, hãy cập nhật trình xử lý window.onload để gọi hàm mới này

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

Sau khi bạn đã hoàn tất bước này, mã của bạn phải khớp với nội dung trong thư mục 02-fonts của kho lưu trữ phòng thí nghiệm mã.

Nếu bạn tải trang trong một trình duyệt và mở bảng điều khiển dành cho nhà phát triển, bạn sẽ thấy một kết quả như sau:

Ảnh chụp màn hình lúc 11:13,22 sáng ngày 17/05/2016

Việc biết thời điểm hình ảnh có thể hiển thị không đơn giản như khi xuất hiện. Từ những bước trước đó, bạn đã biết rằng các biểu định kiểu và thẻ <script> đồng bộ có thể chặn quá trình kết xuất, phân tích cú pháp và thực thi tập lệnh. Những gì bạn có thể không biết là cả hai đều không chặn trình quét tải trước của trình duyệt.

Trình quét tải trước là công cụ mà tất cả các trình duyệt hiện đại triển khai như một trong nhiều nỗ lực để cải thiện hiệu suất, ngay cả trên các trang web không chú trọng và chứa nhiều tài sản chặn. Ý tưởng là mặc dù một số nội dung có thể chặn phân tích cú pháp hoặc hiển thị hoặc thực thi tập lệnh, nhưng chúng không phải chặn các nội dung tải xuống. Vì vậy, trình duyệt quét tệp HTML trước khi bắt đầu xây dựng DOM và tìm kiếm nội dung mà trình duyệt có thể bắt đầu tải xuống ngay lập tức.

Đối với hình ảnh, điều này có nghĩa là có nhiều khả năng hình ảnh của bạn có thể đã được tải xuống vào thời điểm thêm hình ảnh vào DOM. Điều này cũng có nghĩa là điểm mà hình ảnh được tải xuống không nhất thiết là chỉ số hiệu suất tốt. Chỉ số hiệu suất mà bạn cần quan tâm là điểm mà hình ảnh hiển thị cho người dùng.

Khi hình ảnh được tải xuống trước khi được thêm vào DOM, điểm mà hình ảnh này hiển thị là điểm mà tại đó hình ảnh sẽ nằm trong DOM. Mặt khác, nếu một hình ảnh không được tải xuống trước khi được thêm vào DOM, thì điểm mà hình ảnh đó sẽ hiển thị là khi trình xử lý của onload kích hoạt.

Vì vậy, để biết thời điểm hình ảnh hiển thị, bạn phải xử lý cả hai trường hợp.

Bạn có thể thực hiện việc này bằng cách thêm dấu vào từng trình xử lý onload của hình ảnh cũng như trong thẻ cùng dòng <script> ngay sau hình ảnh cuối cùng trong DOM. Ý tưởng là dấu xảy ra cuối cùng sẽ là dấu đại diện khi tất cả các hình ảnh hiển thị.

Để thêm dấu thời gian cho cả thời điểm tải hình ảnh và thời điểm hiển thị hình ảnh, hãy cập nhật mã hình ảnh trong index.html như sau:

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

Vì phương thức performance.measure cho tên dấu cụ thể sẽ luôn sử dụng dấu cuối cùng (nếu tìm thấy nhiều dấu có cùng tên), bạn có thể dùng hàm tiện ích measureDuration trong tệp perf-analytics.js mà không cần sửa đổi thêm:

/**
 * 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'));
}

Thêm hàm trên vào tệp perf-analytics.js rồi cập nhật trình xử lý window.onload để gọi hàm đó:

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

Sau khi bạn đã hoàn tất bước này, mã của bạn phải khớp với nội dung trong thư mục 03-images của kho lưu trữ phòng thí nghiệm mã.

Nếu bạn tải trang trong một trình duyệt và mở bảng điều khiển dành cho nhà phát triển, bạn sẽ thấy một kết quả như sau:

Ảnh chụp màn hình lúc 11:13,39 sáng ngày 17/5/2016

Vì các thẻ <script> không có thuộc tính async chặn DOM phân tích cú pháp DOM cho đến khi cả hai thẻ này đều được tải xuống và thực thi, bạn có thể xác định điểm mà tất cả các tập lệnh đã thực thi xong bằng cách thêm dấu trong thẻ tập lệnh cùng dòng ngay sau <script> đồng bộ cuối cùng trong DOM.

Xin lưu ý rằng việc sử dụng trình xử lý onload sẽ không hoạt động trong trường hợp này vì trình duyệt phải thực thi tập lệnh sau khi tải và mất thời gian. Tập lệnh nhanh tải nhưng chậm thực thi có thể kém như tập lệnh tải chậm.

Để theo dõi khi tất cả JavaScript được tải và thực thi trong tài liệu chính, hãy cập nhật phần JavaScript trong index.html với mã sau:

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

Thao tác này sẽ thêm một nhãn có tên "js:execute" ngay sau khi các tập lệnh cho trình bổ trợ jQuery và Bootstrap\39; đã tải xuống và hoàn tất việc thực thi.

Để đo lường khoảng thời gian này, hãy thêm hàm sau vào 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'));
}

Sau đó, gọi hàm này từ trình xử lý window.onload:

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

Sau khi bạn đã hoàn tất bước này, mã của bạn phải khớp với nội dung trong thư mục 04-javascript của kho lưu trữ phòng thí nghiệm mã.

Nếu bạn tải trang trong một trình duyệt và mở bảng điều khiển dành cho nhà phát triển, bạn sẽ thấy một kết quả như sau:

Ảnh chụp màn hình lúc 11 giờ 14 phút 3 ngày 5 tháng 5 năm 2016

Không phải tất cả các trình duyệt đều hỗ trợ lời hứa JavaScript hoặc API thời gian người dùng và nếu bạn chạy mã mà bạn viết cho đến nay trong một trình duyệt không hỗ trợ một trong các tính năng này, bạn sẽ gặp lỗi.

Để giải quyết vấn đề này, bạn có thể sử dụng tính năng phát hiện tính năng. Thêm đoạn mã theo dõi ngay trước phần phông chữ. Dòng JavaScript này phát hiện sự hỗ trợ cho phương thức performance.mark, do đó, phương thức này phải được thêm vào trang trước khi phương thức đó được sử dụng:

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

Tiếp theo, ở bất kỳ đâu trong index.html mà bạn gọi là performance.mark, hãy thêm tiền tố đó vào tính năng phát hiện tính năng. Đây là một ví dụ cập nhật mã trong khối hình ảnh, nhưng bạn cũng phải đảm bảo cập nhật các phần khác.

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

Giờ đây, biến __perf được đặt trên window. Bạn cũng có thể sử dụng biến này trong perf-analytics.js để đảm bảo không gọi một phương thức không được hỗ trợ trong trình duyệt hiện tại.

Bạn phải cập nhật hàm measureDuration để thêm điều kiện sau:

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

Cuối cùng, vì không phải tất cả các trình duyệt đều hỗ trợ lời hứa JavaScript, hàm measureWebfontPerfAndFailures cũng phải được bao bọc trong một điều kiện:

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

Bây giờ bạn sẽ có thể chạy mã trong bất kỳ trình duyệt nào mà không gặp vấn đề.

Sau khi bạn đã hoàn tất bước này, mã của bạn phải khớp với nội dung trong thư mục 05-feature-detects của kho lưu trữ phòng thí nghiệm mã.

Bước cuối cùng trong lớp học lập trình này là lấy dữ liệu đang được ghi lại vào bảng điều khiển và thay vào đó gửi dữ liệu đó đến Google Analytics. Ngoài ra, trước khi có thể gửi dữ liệu đến Google Analytics, bạn phải thêm thư viện analytics.jsđoạn mã theo dõi mặc định vào trang của mình.

Thêm mã sau vào index.html sau khối JavaScript chính nhưng trước khi tập lệnh perf-analytics.js được tải:

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

Nếu bạn đã thêm Google Analytics vào một trang web trước đây, bạn sẽ biết rằng bạn phải thay thế "UA-XXXXX-Y" phần giữ chỗ bằng ID theo dõi mà bạn nhận được khi tạo một thuộc tính mới trong Google Analytics.

Đoạn mã theo dõi analytics.js thực hiện bốn việc chính:

  • Tạo một phần tử <script> không đồng bộ để tải thư viện JavaScript analytics.js xuống.
  • Khởi tạo hàm ga() toàn cầu (được gọi là hàng đợi lệnh ga()) cho phép bạn lên lịch chạy các lệnh sau khi thư viện analytics.js được tải và sẵn sàng hoạt động.
  • Thêm một lệnh vào hàng đợi lệnh ga() để tạo một đối tượng trình theo dõi mới cho thuộc tính được chỉ định thông qua tham số#39;UA-XXXXX-Y×39;.
  • Thêm một lệnh khác vào hàng đợi lệnh ga() để gửi lượt xem trang đến Google Analytics cho trang hiện tại.

Mặc dù dữ liệu được thu thập chỉ từ lượt xem trang rất hữu ích, nhưng dữ liệu này không nói rõ toàn bộ câu chuyện. Để hiểu rõ hơn về cách người dùng đang trải nghiệm trang web hoặc ứng dụng của bạn, bạn phải gửi thêm dữ liệu tương tác tới Google Analytics.

Google Analytics hỗ trợ một số loại dữ liệu tương tác: lượt xem trang, sự kiện, lượt tương tác trên mạng xã hội, trường hợp ngoại lệthời gian người dùng gần đây nhất. Để gửi dữ liệu thời gian của người dùng đến Google Analytics, bạn có thể sử dụng chữ ký lệnh sau:

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

Trong đó timingCategory là một chuỗi cho phép bạn sắp xếp các lần truy cập thời gian vào nhóm logic, timingVar là biến bạn đang đo và timingValue là thời lượng thực tế tính bằng mili giây.

Để xem cách hoạt động của thực tế này, câu lệnh console.log trong hàm measureCssUnblockTime có thể được cập nhật như sau:

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

Mặc dù mã trên sẽ hoạt động trong một số trường hợp, bạn cần lưu ý hai điều quan trọng sau:

  • Bước trước đó đã cập nhật hàm measureDuration để chỉ chạy nếu trình duyệt hỗ trợ API Thời gian người dùng, nghĩa là đôi khi trình duyệt sẽ trả về undefined. Vì không có lý do gì để gửi dữ liệu không xác định đến Google Analytics (trong một số trường hợp, dữ liệu này thậm chí có thể làm rối mã nguồn của bạn), bạn chỉ nên gửi lần truy cập thời gian này nếu measureDuration trả về một giá trị.
  • Khi measureDuration trả về một giá trị, giá trị này phải là DOMHighResTimeStamp. Giá trị này sẽ có độ chính xác lớn hơn mili giây. Vì timingValue trong Google Analytics phải là số nguyên, nên bạn phải làm tròn giá trị do measureDuration trả về.

Để tính đến các giá trị này, hãy cập nhật câu lệnh trả về trong hàm measureDuration để làm tròn giá trị trả về:

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

Và cập nhật các lệnh thời gian để chỉ chạy nếu có giá trị cho chỉ số đang được đề cập. Ví dụ: bạn nên cập nhật hàm measureCssUnblockTime thành mã như sau:

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

Bạn cần thực hiện các nội dung cập nhật tương tự cho tất cả các chức năng đo lường khác. Sau khi hoàn tất, tệp perf-analytics.js cuối cùng sẽ có dạng như sau:

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);
  }
}

Và tệp index.html cuối cùng sẽ có dạng như sau:

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

Nếu bạn tải trang này và xem các yêu cầu trong bảng điều khiển mạng, bạn sẽ thấy nội dung như sau:

Ảnh chụp màn hình lúc 6.57.23 chiều.2016-05-10

Việc này rất hữu ích, nhưng bạn có thể rắc rối khi xem dữ liệu này dưới dạng một yêu cầu mã hóa URL. Và nếu vì bất kỳ lý do gì mà bạn không thấy những yêu cầu này, thì sẽ thực sự khó để theo dõi nơi xảy ra lỗi.

Một cách tiếp cận hiệu quả hơn khi phát triển cục bộ là sử dụng phiên bản gỡ lỗi của analytics.js. Phiên bản này sẽ ghi lại thông tin gỡ lỗi hữu ích vào bảng điều khiển khi mỗi lệnh analytics.js được chạy. Nếu bạn

cập nhật URL analytics.js trong index.html thành analytics_debug.js và mở bảng điều khiển trình duyệt, bạn sẽ thấy các câu lệnh được in ra như sau:

Ảnh chụp màn hình lúc 6.54.13 chiều.2016-05-10 chiều.

Giờ đây, bạn đã hiểu cách triển khai tính năng đo lường hiệu suất cho trang trình diễn này. Bạn có thể thử thêm trang này vào trang web của mình và gửi dữ liệu người dùng thực cho Google Analytics.

Báo cáo về dữ liệu mà bạn thu thập

Sau khi bạn đã thu thập dữ liệu hiệu suất trong một vài ngày, bạn sẽ có thể báo cáo về dữ liệu đó để có được thông tin chi tiết có thể hành động về tốc độ tải trang web và các tài nguyên của trang web đối với người dùng thực.

Để truy cập báo cáo Thời gian người dùng trong Google Analytics, hãy nhấp vào tab Báo cáo ở trên cùng và chọn "Hành vi > Tốc độ trang và gt; Thời gian người dùng&quo; từ điều hướng thanh bên (hoặc làm theo hướng dẫn để xem báo cáo Thời gian người dùng từ Trung tâm trợ giúp).

Khi tải báo cáo Thời gian người dùng trong Google Analytics, bạn sẽ có thể thấy các danh mục thời gian tương ứng với dữ liệu mà bạn đã gửi. Hãy nhấp vào một thử nghiệm bất kỳ trong số đó để xem hình ảnh chi tiết về dữ liệu thời gian của bạn. Hình ảnh tiếp theo là ví dụ về thời gian tải phông chữ trên một trang web thực sử dụng Google Fonts trong 24 giờ qua.

Ảnh chụp màn hình lúc 7.16.07 PM.png 2016-05-10

Xin chúc mừng! Bạn đã hoàn thành thành công phòng thí nghiệm mã này. Nếu bạn muốn tìm hiểu sâu hơn, phần tiếp theo sẽ cung cấp cho bạn một số đề xuất về cách xây dựng dựa trên mã này để biết thêm thông tin chi tiết.

Các chỉ số hiệu suất được đề cập trong phòng thí nghiệm mã này rất quan trọng để đo lường cách trang web của bạn tải cho người dùng thực, nhưng các chỉ số này mới chỉ là khởi đầu. Nếu bạn muốn tìm hiểu sâu hơn về số liệu phân tích về hiệu suất, bước tiếp theo đơn giản là theo dõi các chỉ số khác.

Trong phòng thí nghiệm mã này, bạn theo dõi các chỉ số liên quan đến thời điểm tài nguyên được cung cấp cho người dùng. Nếu muốn, bạn có thể chia nhỏ hầu hết các trường hợp này. Ví dụ: thay vì chỉ đo lường khi JavaScript thực thi xong, bạn có thể đo lường khi JavaScript bắt đầu tải, khi tải xong, khi bắt đầu thực thi và cuối cùng là khi JavaScript thực thi xong. Từng chỉ số này có thể phát hiện vấn đề mà chỉ một trong số đó có thể không biết.

Ngoài việc chi tiết hơn, bạn cũng nên suy nghĩ chủ động hơn về chiến lược phân tích hiệu suất chung. Mục tiêu là gì? Thành công là gì?

Khi nói đến bất kỳ loại phân tích nào, bạn thường sẽ bắt đầu với một loại câu hỏi và sau đó tìm cách sử dụng dữ liệu để trả lời câu hỏi đó.

Ví dụ: hãy xem xét danh sách câu hỏi sau đây và cách bạn sử dụng kiến thức đã học được trong phòng thí nghiệm mã này để sử dụng số liệu phân tích nhằm trả lời chúng:

  • Giá trị của những chỉ số mà tính năng theo dõi của bạn giảm hay tăng theo thời gian?
  • Việc sử dụng bộ nhớ đệm ngoại tuyến thông qua trình chạy dịch vụ hoặc bộ nhớ cục bộ có ảnh hưởng như thế nào đến hiệu suất tổng thể của trang web của bạn?
  • Tài nguyên của bạn có đang được tải một cách tối ưu không? Tức là có khoảng cách lớn giữa thời điểm tài nguyên được tải xuống và thời điểm tài nguyên có sẵn để sử dụng?
  • Có mối tương quan nào giữa hiệu suất và các chỉ số khác mà bạn đang theo dõi không (ví dụ: tỷ lệ đăng ký, thời gian trên trang web, số lượt mua hàng, v.v.)?

Cuối cùng, nếu bạn muốn tìm hiểu thêm về hiệu suất web hoặc Google Analytics, dưới đây là một số tài nguyên tuyệt vời để giúp bạn bắt đầu: