Mierzenie najważniejszych wskaźników wydajności za pomocą Google Analytics

Z tego laboratorium dowiesz się, jak za pomocą Google Analyticsinterfejsu User Timings API mierzyć rzeczywistą wydajność witryny lub aplikacji i optymalizować ją, aby zwiększać komfort użytkowników.

Narzędzia takie jak WebPagetest.org to dobry początek optymalizacji wydajności, ale prawdziwym testem wydajności witryny zawsze będą rzeczywiste dane od faktycznych użytkowników.

Jeśli prowadzisz witrynę, prawdopodobnie używasz już Google Analytics do pomiaru ruchu, a także takich danych jak użycie urządzeń i przeglądarek. Wystarczy dodać trochę kodu, aby uwzględnić w nich dane o skuteczności.

Czego się nauczysz

  • Jak dokładnie i skutecznie mierzyć dane o skuteczności za pomocą interfejsu User Timings API
  • Jak wysyłać te dane do Google Analytics, aby można było je uwzględniać w raportach

Czego potrzebujesz

Jak zamierzasz wykorzystać ten samouczek?

Tylko przeczytaj Przeczytaj i wykonaj ćwiczenia

Jak oceniasz swoje doświadczenie w tworzeniu witryn lub aplikacji?

Początkujący Średnio zaawansowany Zaawansowany

Możesz pobrać cały przykładowy kod na komputer...

Pobierz plik ZIP

…lub sklonuj repozytorium GitHub z poziomu wiersza poleceń.

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

Przykładowy kod jest podzielony na podkatalogi odpowiadające poszczególnym numerowanym krokom w tym module. Możesz go używać, aby łatwo przechodzić między sekcjami w tym laboratorium kodu lub sprawdzać, czy implementacja jest prawidłowa.

Jeśli masz dostęp do programu do porównywania plików, możesz go użyć, aby zobaczyć, co dokładnie zmieniło się w poszczególnych krokach.

W tym module nauczysz się, jak przekształcić pojedynczy plik HTML, który wczytuje te komponenty:

  • Czcionki internetowe
  • Arkusze stylów
  • Obrazy
  • JavaScript

Napiszesz też nowy kod, który będzie mierzyć kluczowe dane o skuteczności każdego z tych typów komponentów.

Uwagi dotyczące skuteczności komponentów

Jeśli kiedykolwiek czytałeś(-aś) coś o optymalizacji wydajności, prawdopodobnie wiesz już, że każdy z tych typów komponentów ma swoje własne cechy i może wpływać na ogólną postrzeganą wydajność na różne sposoby.

CSS

Na przykład arkusze stylów blokują renderowanie wszystkich elementów w DOM, które znajdują się za nimi. Oznacza to, że przeglądarka musi wysłać żądanie arkusza stylów, pobrać go i przeanalizować, zanim będzie mogła wyrenderować dowolną treść w DOM, która znajduje się za nim. Z tego powodu arkusze stylów najlepiej umieszczać w sekcji <head> dokumentu. Ze względu na blokujący charakter CSS często zaleca się umieszczanie w sekcji <head> tylko najważniejszego kodu CSS, a następnie asynchroniczne ładowanie pozostałego kodu CSS.

JavaScript

JavaScript nie blokuje renderowania, ale blokuje analizowanie i tworzenie DOM. Jest to konieczne, ponieważ JavaScript może modyfikować DOM, co oznacza, że za każdym razem, gdy przeglądarka napotka tag <script> (z wyjątkiem skryptów asynchronicznych), musi wykonać kod przed przejściem do następnego tagu. Jeśli tag <script> odwołuje się do zewnętrznego pliku JavaScript, musi pobrać i wykonać kod przed przejściem dalej.

Z tego powodu często zaleca się, aby kod JavaScript był ładowany tuż przed tagiem zamykającym </body>, dzięki czemu większość DOM będzie dostępna jak najszybciej.

Czcionki internetowe

Jeśli wczytujesz czcionki internetowe, możesz też zablokować renderowanie dokumentu, dopóki czcionka nie będzie dostępna. W takim przypadku kluczowe jest zrozumienie, ile czasu zajmuje to użytkownikom. Czcionka internetowa może ładować się szybko u Ciebie, ale bardzo wolno u większości osób odwiedzających Twoją witrynę. Dlatego tak ważne jest prowadzenie pomiarów i podejmowanie decyzji na podstawie rzeczywistych danych.

Obrazy

Obrazy mogą ożywić witrynę, ale często najdłużej się wczytują. Zrozumienie, co to właściwie oznacza, i umiejętność dostrzegania korelacji między wzorcami użytkowania a czasem wczytywania strony ma kluczowe znaczenie dla optymalizacji.

Pierwszym krokiem w tym laboratorium kodu jest sprawdzenie, jak wygląda strona demonstracyjna przed dodaniem kodu pomiaru wydajności.

Aby wyświetlić wersję demonstracyjną, utwórz nowy folder i dodaj do niego plik o nazwie index.html. Następnie skopiuj poniższy kod i wklej go do pliku 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>

Następnie otwórz Web Server for Chrome i uruchom lokalny serwer w utworzonym przed chwilą katalogu. Upewnij się, że opcja „automatycznie wyświetlaj plik index.html” jest zaznaczona.

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

W przeglądarce powinien być teraz dostępny adres http://127.0.0.1:8887/, pod którym zobaczysz plik demonstracyjny. Powinna wyglądać mniej więcej tak:

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

Po uruchomieniu strony demonstracyjnej poświęć chwilę na przyjrzenie się kodowi i zobacz wszystkie różne typy zasobów, które są wczytywane. W kilku następnych krokach dodasz kod, który będzie mierzyć, kiedy te komponenty są wczytywane i kiedy użytkownik może z nich korzystać.

Jak wspomnieliśmy wcześniej w sekcji dotyczącej kwestii związanych ze skutecznością komponentów, CSS blokuje renderowanie elementów DOM oraz wykonywanie skryptów, które znajdują się po nim w DOM.

Utworzony właśnie plik demonstracyjny zawiera ten kod CSS, który odwołuje się do Bootstrapa i kilku stylów wbudowanych.

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

Ponieważ CSS blokuje zarówno renderowanie elementów DOM, jak i wykonywanie skryptów, można określić, kiedy CSS przestanie blokować, dodając tag <script> bezpośrednio po CSS, który przechowuje bieżący czas.

Możesz to zrobić, tworząc zmienną i przypisując do niej wartość new Date(), ale dzięki interfejsowi User Timings API istnieje znacznie łatwiejszy sposób: metoda performance.mark.

Aby oznaczyć moment, w którym CSS przestaje blokować renderowanie i wykonywanie skryptów, dodaj ten wiersz kodu tuż przed zamykającym komentarzem <!-- End CSS -->.

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

Metoda performance.mark tworzy sygnaturę czasową o wysokiej rozdzielczości w tym konkretnym momencie i przypisuje ją do nazwy przekazanej do metody. W tym przypadku nazwa znacznika to „css:unblock”.

Metoda performance.mark jest ściśle powiązana z metodą performance.measure, która służy do obliczania różnicy czasu między 2 znacznikami (oprócz znaczników, które tworzysz, możesz też używać znaczników, które przeglądarka tworzy automatycznie w różnych punktach interfejsu Navigation Timing API).

Ta funkcja narzędziowa mierzy i zwraca czas, który upłynął od dodanego przez Ciebie znacznika do znacznika responseEnd utworzonego przez interfejs Navigation Timing API.

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

Aby zacząć korzystać z tej funkcji narzędzia, utwórz nowy plik o nazwie perf-analytics.js (w tym samym katalogu co plik index.html) i skopiuj do niego powyższy kod.

Po zdefiniowaniu tej funkcji możesz ją wywołać i przekazać nazwę znacznika „css:unblock”. Aby nie zakłócać wczytywania innych zasobów, pomiary te należy odłożyć do momentu wywołania zdarzenia wczytywania okna.

Po napisaniu funkcji wywołującej ten kod plik perf-analytics.js powinien wyglądać mniej więcej tak:

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

Na koniec musisz wczytać skrypt perf-analytics.jsindex.html. Aby to zrobić, dodaj do głównego dokumentu ten tag skryptu. Dodaj go na końcu, aby nie zakłócał ładowania innych zasobów.

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

Gdy to zrobisz, Twój kod powinien być zgodny z kodem w katalogu 01-css w repozytorium z ćwiczeniami.

Jeśli załadujesz stronę w przeglądarce i otworzysz konsolę programisty, zobaczysz dane wyjściowe podobne do tych:

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

Czcionki internetowe są zwykle wczytywane za pomocą zewnętrznego arkusza stylów, jak w początkowym pliku demonstracyjnym:

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

Ponieważ jest to tag <link> do pliku CSS, może się wydawać, że określenie, kiedy czcionki są załadowane i gotowe do użycia, jest tak proste, jak dodanie znacznika w tagu <script> bezpośrednio po tagu <link>, tak jak w kroku 1.

Niestety nie jest to takie proste.

Arkusz stylów blokuje wykonywanie JavaScriptu, ponieważ jego zawartość jest używana do tworzenia CSSOM. Ponieważ wczytywany JavaScript może potrzebować dostępu do CSSOM, jego wykonanie musi zostać opóźnione do momentu pełnego utworzenia CSSOM.

Problem polega na tym, że przeglądarka może utworzyć CSSOM bez pobierania czcionki. Oznacza to, że jeśli dodasz znacznik do DOM za pomocą tagu skryptu wbudowanego bezpośrednio po tagu <link> arkusza stylów czcionki, znacznik prawdopodobnie pojawi się zanim czcionka zostanie w pełni wczytana.

Dopóki zdarzenia ładowania czcionek nie będą dostępne w przeglądarkach, do określania, kiedy czcionka jest rzeczywiście aktywna i gotowa do użycia na stronie, potrzebny jest JavaScript. Na szczęście wczytywanie czcionek za pomocą JavaScriptu również zwiększa wydajność, ponieważ nie wymaga dodatkowego blokującego żądania do pliku CSS.

Większość czcionek internetowych (w tym czcionki Google, Typekit i font.com) można wczytywać za pomocą skryptu webfont.js, który został opracowany wspólnie przez Google i Typekit.

Aby zaktualizować dokument główny i używać webfont.js do wczytywania czcionek (zamiast tagu <link>), zastąp sekcję czcionek w kodzie tym fragmentem:

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

W powyższym kodzie warto zwrócić uwagę na 2 ważne kwestie:

  • W aktywnym wywołaniu zwrotnym tworzy znacznik „fonts:active”, dzięki czemu możesz później zmierzyć, ile czasu zajęło wczytanie czcionek.
  • Tag <script>, który wczytuje plik webfonts.js, zawiera atrybut async, więc nie blokuje analizowania ani renderowania pozostałej części dokumentu (co nie jest prawdą w przypadku tagów <link>).

Powyższe kody tworzą wprawdzie znacznik „fonts:active”, ale jego pomiar i rejestrowanie w konsoli nie jest tak proste jak w przypadku znacznika „css:unblock”. Dzieje się tak, ponieważ ładowanie czcionki odbywa się teraz asynchronicznie, więc jeśli spróbujesz zmierzyć znacznik „fonts:active” w procedurze obsługi window.onload (tak jak w przypadku „css:unblock”), jest bardzo prawdopodobne, że czcionka nie będzie jeszcze załadowana.

Aby rozwiązać ten problem, możesz utworzyć obietnicę, która zostanie spełniona po załadowaniu czcionki. Poniższa funkcja wykonuje to za Ciebie. Skopiuj go i wklej do pliku 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')
  });
}

Zaktualizuj też moduł obsługi window.onload, aby wywoływał tę nową funkcję.

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

Gdy to zrobisz, Twój kod powinien być zgodny z kodem w katalogu 02-fonts w repozytorium z ćwiczeniami.

Jeśli załadujesz stronę w przeglądarce i otworzysz konsolę programisty, zobaczysz dane wyjściowe podobne do tych:

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

Określenie, kiedy obraz jest widoczny, nie jest tak proste, jak mogłoby się wydawać. Z poprzednich kroków wiesz, że arkusze stylów i synchroniczne tagi <script> mogą blokować renderowanie, analizowanie i wykonywanie skryptów. Możesz nie wiedzieć, że żadna z nich nie blokuje skanera wstępnego ładowania przeglądarki.

Skaner wstępnego załadowania jest implementowany przez wszystkie nowoczesne przeglądarki w ramach wielu prób poprawy wydajności, nawet w przypadku witryn, które nie są zoptymalizowane pod kątem wydajności i zawierają wiele zasobów blokujących. Chodzi o to, że niektóre komponenty mogą blokować parsowanie, renderowanie lub wykonywanie skryptu, ale nie muszą blokować pobierania. Dlatego przeglądarka skanuje plik HTML, zanim zacznie tworzyć DOM, i szuka zasobów, które może od razu pobrać.

W przypadku obrazów oznacza to, że istnieje duże prawdopodobieństwo, że obrazy zostaną pobrane, zanim zostaną dodane do DOM. Oznacza to również, że moment pobrania obrazu niekoniecznie jest dobrym wskaźnikiem skuteczności. Wskaźnik wydajności, na którym należy się skupić, to moment, w którym obraz jest widoczny dla użytkownika.

Gdy obraz zostanie pobrany przed dodaniem do DOM, moment, w którym stanie się widoczny, to moment, w którym znajdzie się w DOM. Z drugiej strony, jeśli obraz nie zostanie pobrany przed dodaniem do DOM, stanie się widoczny w momencie uruchomienia modułu obsługi onload.

Aby wiedzieć, kiedy obraz jest widoczny, musisz obsłużyć oba przypadki.

Możesz to zrobić, dodając znaczniki w procedurze obsługi zdarzenia onload każdego obrazu oraz w tagu wbudowanym <script> bezpośrednio po ostatnim obrazie w DOM. Chodzi o to, że ostatni znacznik będzie oznaczać moment, w którym wszystkie obrazy są widoczne.

Aby dodać znaczniki zarówno w momencie wczytania obrazów, jak i w momencie ich renderowania, zaktualizuj kod obrazów w index.html w ten sposób:

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

Ponieważ metoda performance.measure dla określonej nazwy znacznika zawsze używa ostatniego znacznika (jeśli znajdzie wiele znaczników o tej samej nazwie), można w tym celu użyć funkcji narzędziowej measureDuration w pliku perf-analytics.js bez żadnych dodatkowych modyfikacji:

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

Dodaj powyższą funkcję do pliku perf-analytics.js, a następnie zmodyfikuj moduł obsługi window.onload, aby ją wywoływał:

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

Gdy to zrobisz, Twój kod powinien być zgodny z kodem w katalogu 03-images w repozytorium z ćwiczeniami.

Jeśli załadujesz stronę w przeglądarce i otworzysz konsolę programisty, zobaczysz dane wyjściowe podobne do tych:

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

Tagi <script> bez atrybutu async blokują analizowanie DOM, dopóki nie zostaną pobrane i wykonane. Możesz określić moment, w którym wszystkie skrypty zostaną wykonane, dodając znacznik w tagu skryptu wbudowanego bezpośrednio po ostatnim synchronicznym tagu <script> w DOM.

Pamiętaj, że w tym przypadku użycie modułów obsługi onload nie zadziała, ponieważ przeglądarka musi wykonać skrypt po jego załadowaniu, a to zajmuje trochę czasu. Skrypt, który szybko się wczytuje, ale wolno wykonuje, może być równie zły jak skrypt, który wczytuje się powoli.

Aby śledzić, kiedy cały kod JavaScript w głównym dokumencie zostanie wczytany i wykonany, zaktualizuj sekcję JavaScript w index.html, używając tego kodu:

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

Spowoduje to dodanie znacznika o nazwie „js:execute” natychmiast po pobraniu i wykonaniu skryptów wtyczek jQuery i Bootstrap.

Aby zmierzyć czas trwania tego procesu, dodaj do perf-analytics.js tę funkcję:

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

Następnie wywołaj go z poziomu funkcji obsługi window.onload:

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

Gdy to zrobisz, Twój kod powinien być zgodny z kodem w katalogu 04-javascript w repozytorium z ćwiczeniami.

Jeśli załadujesz stronę w przeglądarce i otworzysz konsolę programisty, zobaczysz dane wyjściowe podobne do tych:

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

Nie wszystkie przeglądarki obsługują obietnice JavaScriptu lub interfejsy API User Timing. Jeśli uruchomisz napisany do tej pory kod w przeglądarce, która nie obsługuje jednej z tych funkcji, pojawią się błędy.

Aby sobie z tym poradzić, możesz użyć wykrywania funkcji. Dodaj ten fragment kodu bezpośrednio przed sekcją czcionek. Ten wiersz kodu JavaScript wykrywa obsługę metody performance.mark, więc musi zostać dodany do strony przed użyciem tej metody:

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

Następnie w dowolnym miejscu w index.html, w którym wywołujesz performance.mark, dodaj przedrostek wykrywania funkcji. Oto przykład aktualizacji kodu w bloku obrazu. Pamiętaj jednak, aby zaktualizować też inne sekcje.

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

Teraz, gdy zmienna __perf jest ustawiona na window, możesz jej też używać w perf-analytics.js, aby mieć pewność, że nie wywołujesz metody, która nie jest obsługiwana w bieżącej przeglądarce.

Funkcję measureDuration należy zaktualizować, aby dodać ten warunek:

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

Na koniec, ponieważ nie wszystkie przeglądarki obsługują obietnice JavaScriptu, funkcja measureWebfontPerfAndFailures musi być również zawarta w instrukcji warunkowej:

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

Teraz możesz bez problemu uruchamiać kod w dowolnej przeglądarce.

Gdy to zrobisz, Twój kod powinien być zgodny z kodem w katalogu 05-feature-detects w repozytorium z ćwiczeniami.

Ostatnim krokiem w tym samouczku jest przekierowanie danych, które są rejestrowane w konsoli, do Google Analytics. Zanim zaczniesz wysyłać dane do Google Analytics, musisz dodać do strony bibliotekę analytics.jsdomyślny fragment kodu śledzenia.

Dodaj ten kod do pliku index.html po głównym bloku JavaScript, ale przed załadowaniem skryptu perf-analytics.js:

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

Jeśli wcześniej dodawałeś(-aś) Google Analytics do witryny, wiesz, że musisz zastąpić symbol zastępczy „UA-XXXXX-Y” identyfikatorem śledzenia, który został Ci przydzielony podczas tworzenia nowej usługi w Google Analytics.

Fragment kodu śledzenia analytics.js wykonuje 4 główne czynności:

Dane zebrane na podstawie samych wyświetleń stron są przydatne, ale nie pokazują wszystkiego. Aby lepiej poznać wrażenia użytkowników dotyczące Twojej witryny lub aplikacji, musisz wysyłać do Google Analytics dodatkowe dane o interakcjach.

Google Analytics obsługuje kilka typów danych o interakcjach: wyświetlenia stron, zdarzenia, interakcje społecznościowe, wyjątki i (co nie mniej ważne) czasy użytkownika. Aby wysyłać dane o czasie działania użytkownika do Google Analytics, możesz użyć tego podpisu polecenia:

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

gdzie timingCategory to ciąg znaków, który umożliwia uporządkowanie wyników pomiaru czasu w logiczne grupy, timingVar to mierzona zmienna, a timingValue to rzeczywisty czas trwania w milisekundach.

Aby zobaczyć, jak to działa w praktyce, instrukcję console.log w funkcji measureCssUnblockTime można zaktualizować w ten sposób:

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

Powyższy kod będzie działać w niektórych sytuacjach, ale musisz pamiętać o 2 ważnych kwestiach:

  • W poprzednim kroku zaktualizowaliśmy funkcję measureDuration, aby była uruchamiana tylko wtedy, gdy przeglądarka obsługuje interfejs User Timings API, co oznacza, że czasami będzie zwracać wartość undefined. Nie ma powodu, aby wysyłać do Google Analytics niezdefiniowane dane (w niektórych przypadkach może to nawet zepsuć raporty), więc ten pomiar czasu należy wysyłać tylko wtedy, gdy measureDuration zwraca wartość.
  • Gdy funkcja measureDuration zwraca wartość, jest to DOMHighResTimeStamp, która ma dokładność większą niż milisekunda. W Google Analytics wartość timingValue musi być liczbą całkowitą, więc musisz zaokrąglić wartość zwracaną przez measureDuration.

Aby uwzględnić te pułapki, zaktualizuj instrukcję return w funkcji measureDuration, aby zaokrąglić zwracaną wartość:

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

Zaktualizuj polecenia dotyczące czasu, aby były wykonywane tylko wtedy, gdy istnieje wartość dla danego rodzaju danych. Na przykład funkcja measureCssUnblockTime powinna zostać zaktualizowana w ten sposób:

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

Podobne zmiany musisz wprowadzić we wszystkich pozostałych funkcjach pomiaru. Po zakończeniu pracy plik perf-analytics.js powinien wyglądać tak:

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

Ostateczny plik index.html powinien wyglądać tak:

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

Jeśli załadujesz tę stronę i sprawdzisz żądania w panelu sieci, zobaczysz coś takiego:

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

Jest to przydatne, ale przeglądanie tych danych w postaci zakodowanego adresu URL może być uciążliwe. Jeśli z jakiegokolwiek powodu nie widzisz tych żądań, bardzo trudno jest ustalić, gdzie wystąpił problem.

Podczas tworzenia lokalnego lepszym rozwiązaniem jest użycie wersji analytics.js do debugowania, która rejestruje w konsoli przydatne informacje do debugowania podczas wykonywania każdego polecenia analytics.js. Jeśli

Jeśli zaktualizujesz adres URL analytics.js w index.html na analytics_debug.js i otworzysz konsolę przeglądarki, zobaczysz wydrukowane instrukcje podobne do tych:

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

Teraz, gdy wiesz już, jak wdrożyć pomiar wydajności na tej stronie demonstracyjnej, możesz spróbować dodać go do swojej witryny i wysyłać dane o prawdziwych użytkownikach do Google Analytics.

Generowanie raportów na podstawie zebranych danych

Po kilku dniach zbierania danych o skuteczności możesz generować raporty na ich podstawie, aby uzyskać przydatne informacje o tym, jak szybko Twoja witryna i jej zasoby wczytują się u prawdziwych użytkowników.

Aby otworzyć raporty o czasie działania użytkownika w Google Analytics, kliknij u góry kartę Raportowanie i w menu bocznym wybierz „Zachowanie > Szybkość witryny > Czas działania użytkownika” (lub postępuj zgodnie z instrukcjami wyświetlania raportu Czas działania użytkownika w Centrum pomocy).

Gdy wczytasz w Google Analytics raport Czas działania użytkownika, powinny być w nim widoczne kategorie czasu działania odpowiadające wysłanym przez Ciebie danym. Kliknij dowolną z nich, aby wyświetlić szczegółowe wizualizacje danych o czasie. Poniższy obraz przedstawia przykładowe czasy wczytywania czcionek w prawdziwej witrynie korzystającej z Google Fonts w ciągu ostatnich 24 godzin.

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

Gratulacje! Ten moduł został ukończony. Jeśli chcesz dowiedzieć się więcej, w następnej sekcji znajdziesz kilka sugestii dotyczących rozbudowy tego kodu, aby uzyskać jeszcze więcej informacji.

Dane o wydajności, które omawiamy w tym laboratorium, są kluczowe do pomiaru czasu wczytywania witryny przez rzeczywistych użytkowników, ale to dopiero początek. Jeśli chcesz bardziej szczegółowo analizować skuteczność, możesz po prostu śledzić więcej danych.

W tym module śledziliśmy dane związane z dostępnością zasobów dla użytkownika. Większość z nich możesz podzielić na jeszcze mniejsze części. Zamiast mierzyć tylko czas zakończenia wykonywania kodu JavaScript, możesz mierzyć czas rozpoczęcia wczytywania, zakończenia wczytywania, rozpoczęcia wykonywania i zakończenia wykonywania. Każdy z tych rodzajów danych może ujawnić problem, którego nie widać w przypadku pozostałych.

Oprócz większej szczegółowości warto też bardziej kompleksowo podejść do ogólnej strategii analizy skuteczności. Jakie są cele? Czym jest sukces?

W przypadku analizy zwykle zaczyna się od pytania, a potem szuka się odpowiedzi na nie w danych.

Rozważmy na przykład tę listę pytań i zastanówmy się, jak wykorzystać wiedzę zdobytą w tym laboratorium, aby za pomocą analityki na nie odpowiedzieć:

  • Czy wartości śledzonych przez Ciebie danych z czasem maleją czy rosną?
  • Jak korzystanie z buforowania offline za pomocą service workera lub pamięci lokalnej wpłynie na ogólną wydajność witryny?
  • Czy zasoby są ładowane optymalnie? Czy istnieje duża różnica między momentem pobrania zasobu a momentem, w którym jest on dostępny do użycia?
  • Czy istnieją jakieś korelacje między skutecznością a innymi śledzonymi przez Ciebie rodzajami danych (np.współczynnikiem rejestracji, czasem spędzonym w witrynie, zakupami itp.)?

Jeśli chcesz dowiedzieć się więcej o wydajności witryny lub Google Analytics, zapoznaj się z tymi materiałami: