Pomiar rzeczywistego wpływu mechanizmów Service Worker na rzeczywistą wydajność

Jednym z największych zalet usług działających w tle (przynajmniej pod względem wydajności) jest możliwość aktywnego kontrolowania buforowania zasobów. Aplikacja internetowa, która może przechowywać w pamięci podręcznej wszystkie niezbędne zasoby, powinna wczytywać się znacznie szybciej w przypadku powracających użytkowników. Jak jednak te korzyści wyglądają w przypadku prawdziwych użytkowników? I jak to mierzyć?

Aplikacja internetowa Google I/O (w skrócie IOWA) to progresywna aplikacja internetowa, która korzysta z większości nowych funkcji oferowanych przez serwisy workerów, aby zapewnić użytkownikom bogate wrażenia z użytkowania aplikacji. Firma korzystała też z Google Analytics, aby rejestrować kluczowe dane o skuteczności i wzorcach korzystania z usługi przez dużą i zróżnicowaną grupę użytkowników.

W tym opracowaniu omawiamy, jak IOWA używała Google Analytics do uzyskiwania odpowiedzi na kluczowe pytania dotyczące skuteczności i raportowania rzeczywistego wpływu usług działających w tle.

Zacznij od pytania

Za każdym razem, gdy wdrażasz w witrynie lub aplikacji usługę analityczną, musisz najpierw określić pytania, na które chcesz uzyskać odpowiedzi na podstawie zbieranych danych.

Mieliśmy kilka pytań, na które chcieliśmy uzyskać odpowiedź, ale w ramach tego case study skupimy się na 2 najciekawszych.

1. Czy buforowanie w usługach workera jest wydajniejsze niż istniejące mechanizmy buforowania HTTP dostępne we wszystkich przeglądarkach?

Oczekujemy, że strony wczytują się szybciej w przypadku powracających użytkowników niż nowych, ponieważ przeglądarki mogą przechowywać żądania w pamięci podręcznej i odtwarzać je natychmiast podczas kolejnych wizyt.

Usługa workerów zapewnia alternatywne funkcje buforowania, które dają deweloperom precyzyjną kontrolę nad tym, co i jak jest buforowane. W przypadku IOWA zoptymalizowaliśmy implementację usług workera, aby każdy zasób był przechowywany w pamięci podręcznej, dzięki czemu powracający użytkownicy mogli korzystać z aplikacji całkowicie offline.

Czy jednak jest to lepsze rozwiązanie niż to, co przeglądarka robi już domyślnie? Jeśli tak, o ile? 1

2. Jak service worker wpływa na wczytywanie witryny?

Innymi słowy, jak szybko wydaje się, że strona się wczytuje, niezależnie od rzeczywistego czasu wczytywania mierzonego za pomocą tradycyjnych danych o wczytywaniu strony?

Odpowiadanie na pytania dotyczące wrażeń nie jest łatwym zadaniem, a żaden wskaźnik nie odda w pełni subiektywnych odczuć. Zdecydowanie jednak niektóre dane są lepsze od innych, dlatego warto wybrać te właściwe.

Wybór odpowiednich danych

Google Analytics domyślnie śledzi czas wczytywania stron (za pomocą interfejsu Navigation Timing API) w przypadku 1% użytkowników witryny i udostępnia te dane w postaci danych takich jak Średni czas wczytywania strony.

Średni czas wczytywania strony to dobry wskaźnik do udzielenia odpowiedzi na pierwsze pytanie, ale nie jest on szczególnie przydatny do udzielenia odpowiedzi na drugie. Po pierwsze, zdarzenie load niekoniecznie odpowiada momentowi, w którym użytkownik może wchodzić w interakcję z aplikacją. Co więcej, 2 aplikacje z identycznym czasem wczytywania mogą wydawać się wczytywane znacznie szybciej lub wolniej. Na przykład witryna z ekranem powitalnym lub wskaźnikiem wczytywania prawdopodobnie wczytuje się znacznie szybciej niż witryna, która przez kilka sekund wyświetla tylko pustą stronę.

W IOWA wyświetlaliśmy animację odliczania na ekranie powitalnym, która (według nas) bardzo skutecznie zajmowała użytkowników, gdy reszta aplikacji wczytywała się w tle. Dlatego śledzenie czasu wyświetlania ekranu powitalnego jest znacznie bardziej przydatne do pomiaru postrzeganej wydajności wczytywania. Aby uzyskać tę wartość, wybraliśmy dane Czas do pierwszego wyświetlenia.

Gdy określiliśmy pytania, na które chcemy uzyskać odpowiedzi, i wyznaczyliśmy dane, które pomogą nam je rozwikłać, nadszedł czas na wdrożenie Google Analytics i rozpoczęcie pomiarów.

Implementacja usługi Analytics

Jeśli korzystasz już z Google Analytics, prawdopodobnie znasz zalecany fragment kodu śledzenia JavaScript. Wygląda on następująco:

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

Pierwszy wiersz w powyższym kodzie inicjalizuje globalną funkcję ga() (jeśli jeszcze nie istnieje), a ostatni wiersz asynchronicznie pobiera bibliotekę analytics.js.

Część środkowa zawiera te 2 wiersze:

ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');

Te 2 polecenia umożliwiają śledzenie, które strony odwiedzają użytkownicy, ale nie dają zbyt wielu innych informacji. Jeśli chcesz śledzić dodatkowe interakcje użytkowników, musisz to zrobić samodzielnie.

W przypadku IOWA chcieliśmy śledzić 2 dodatkowe kwestie:

  • Czas od rozpoczęcia wczytywania strony do pojawienia się pikseli na ekranie.
  • Określa, czy stroną steruje skrypt service worker. Dzięki tym informacjom możemy podzielić raporty na segmenty, aby porównać wyniki z użyciem i bez usługi wątek.

Pomiar czasu do pierwszego wyrenderowania

Niektóre przeglądarki rejestrują dokładny czas, w którym pierwszy piksel jest wyświetlany na ekranie, i udostępniają go deweloperom. Porównanie tej wartości z wartością navigationStart podaną przez interfejs API Czas nawigacji pozwala nam bardzo dokładnie określić, ile czasu minęło od momentu, gdy użytkownik wysłał żądanie strony, do momentu, gdy po raz pierwszy zobaczył coś na stronie.

Jak już wspomniałem, czas do pierwszego wyświetlenia jest ważnym wskaźnikiem, ponieważ to pierwszy moment, w którym użytkownik odczuwa szybkość wczytywania witryny. To pierwsze wrażenie, jakie użytkownicy odnoszą.Dobre pierwsze wrażenie może pozytywnie wpłynąć na pozostałe wrażenia użytkownika2

Aby uzyskać wartość pierwszej aktualizacji w przeglądarkach, które ją udostępniają, utworzyliśmy funkcję użyteczności getTimeToFirstPaintIfSupported:

function getTimeToFirstPaintIfSupported() {
  // Ignores browsers that don't support the Performance Timing API.
  if (window.performance && window.performance.timing) {
    var navTiming = window.performance.timing;
    var navStart = navTiming.navigationStart;
    var fpTime;

    // If chrome, get first paint time from `chrome.loadTimes`.
    if (window.chrome && window.chrome.loadTimes) {
      fpTime = window.chrome.loadTimes().firstPaintTime * 1000;
    }
    // If IE/Edge, use the prefixed `msFirstPaint` property.
    // See http://msdn.microsoft.com/ff974719
    else if (navTiming.msFirstPaint) {
      fpTime = navTiming.msFirstPaint;
    }

    if (fpTime && navStart) {
      return fpTime - navStart;
    }
  }
}

Teraz możemy napisać inną funkcję, która wysyła zdarzenie niezwiązane z interakcją z wartością czasu do pierwszego wyświetlenia:3

function sendTimeToFirstPaint() {
  var timeToFirstPaint = getTimeToFirstPaintIfSupported();

  if (timeToFirstPaint) {
    ga('send', 'event', {
      eventCategory: 'Performance',
      eventAction: 'firstpaint',
      // Rounds to the nearest millisecond since
      // event values in Google Analytics must be integers.
      eventValue: Math.round(timeToFirstPaint)
      // Sends this as a non-interaction event,
      // so it doesn't affect bounce rate.
      nonInteraction: true
    });
  }
}

Po napisaniu obu tych funkcji nasz kod śledzenia wygląda tak:

// Creates the tracker object.
ga('create', 'UA-XXXXX-Y', 'auto');

// Sends a pageview for the initial pageload.
ga('send', 'pageview');

// Sends an event with the time to first paint data.
sendTimeToFirstPaint();

Pamiętaj, że w zależności od tego, kiedy kod jest uruchamiany, piksele mogą być już namalowane na ekranie lub nie. Aby mieć pewność, że ten kod zawsze będzie uruchamiany po pierwszym wyświetleniu, opóźniliśmy wywołanie funkcji sendTimeToFirstPaint() do momentu wystąpienia zdarzenia load. Zdecydowaliśmy się opóźnić wysyłanie wszystkich danych analitycznych do momentu załadowania strony, aby te żądania nie kolidowały z wczytywaniem innych zasobów.

// Creates the tracker object.
ga('create', 'UA-XXXXX-Y', 'auto');

// Postpones sending any hits until after the page has fully loaded.
// This prevents analytics requests from delaying the loading of the page.
window.addEventListener('load', function() {
  // Sends a pageview for the initial pageload.
  ga('send', 'pageview');

  // Sends an event with the time to first paint data.
  sendTimeToFirstPaint();
});

Kod powyżej raportuje do Google Analytics liczbę wystąpień firstpaint, ale to tylko połowa historii. Nadal trzeba śledzić stan skryptu service worker, ponieważ w przeciwnym razie nie będziemy mogli porównać czasu pierwszego wyświetlenia strony kontrolowanej przez skrypt service worker i strony niekontrolowanej przez niego.

Określanie stanu skryptu service worker

Aby określić bieżący stan usługi, utworzyliśmy funkcję pomocniczą, która zwraca jedną z 3 wartości:

  • sterowana: stroną steruje skrypt service worker. W przypadku IOWA oznacza to również, że wszystkie zasoby zostały zapisane w pamięci podręcznej, a strona działa offline.
  • Obsługiwane: przeglądarka obsługuje skrypt service worker, ale nie steruje jeszcze stroną. To oczekiwany stan dla nowych użytkowników.
  • Nieobsługiwane: przeglądarka użytkownika nie obsługuje usługi.
function getServiceWorkerStatus() {
  if ('serviceWorker' in navigator) {
    return navigator.serviceWorker.controller ? 'controlled' : 'supported';
  } else {
    return 'unsupported';
  }
}

Ta funkcja uzyskała dla nas stan service workera. Kolejnym krokiem było powiązanie tego stanu z danymi, które wysyłaliśmy do Google Analytics.

Śledzenie danych niestandardowych za pomocą wymiarów niestandardowych

Domyślnie Google Analytics udostępnia wiele sposobów podziału całkowitego ruchu na grupy na podstawie atrybutów użytkownika, sesji lub interakcji. Te atrybuty nazywamy wymiarami. Typowe wymiary, na których zależy deweloperom stron internetowych, to Przeglądarka, System operacyjnyKategoria urządzenia.

Stan usługi to nie wymiar domyślnie udostępniany przez Google Analytics, ale możesz utworzyć własne wymiary niestandardowe i zdefiniować je według własnego uznania.

W przypadku IOWA utworzyliśmy wymiar niestandardowy o nazwie Service Worker Status i ustawiliśmy jego zakres na hit (czyli na interakcję).4 Każdy wymiar niestandardowy utworzony w Google Analytics otrzymuje w tej usłudze swój niepowtarzalny indeks, a w kodzie śledzenia możesz się do niego odwoływać za pomocą tego indeksu. Jeśli np. indeks utworzonego przez nas wymiaru wynosi 1, możemy zaktualizować logikę w ten sposób, aby wysyłać zdarzenie firstpaint z uwzględnieniem stanu workera usługi:

ga('send', 'event', {
  eventCategory: 'Performance',
  eventAction: 'firstpaint',
  // Rounds to the nearest millisecond since
  // event values in Google Analytics must be integers.
  eventValue: Math.round(timeToFirstPaint)
  // Sends this as a non-interaction event,
  // so it doesn't affect bounce rate.
  nonInteraction: true,

  // Sets the current service worker status as the value of
  // `dimension1` for this event.
  dimension1: getServiceWorkerStatus()
});

To działa, ale tylko wiąże stan usługi z tym konkretnym zdarzeniem. Stan Service Worker może być przydatny w przypadku każdej interakcji, dlatego najlepiej uwzględniać go we wszystkich danych wysyłanych do Google Analytics.

Aby uwzględnić te informacje we wszystkich uchwytach (np. we wszystkich wyświetleniach stron, zdarzeniach itp.), ustawiamy wartość wymiaru niestandardowego w samym obiekcie tracker, zanim jakiekolwiek dane zostaną wysłane do Google Analytics.

ga('set', 'dimension1', getServiceWorkerStatus());

Gdy zostanie ustawiona, jest wysyłana ze wszystkimi kolejnymi działaniami w ramach bieżącego wczytania strony. Jeśli użytkownik ponownie załaduje stronę, funkcja getServiceWorkerStatus() zwróci prawdopodobnie nową wartość, która zostanie ustawiona w obiekcie tracker.

Wskazówka dotycząca przejrzystości i czytelności kodu: inne osoby, które będą przeglądać ten kod, mogą nie wiedzieć, co oznacza dimension1, dlatego zawsze warto utworzyć zmienną, która mapuje znaczące nazwy wymiarów na wartości używane przez analytics.js.

// Creates a map between custom dimension names and their index.
// This is particularly useful if you define lots of custom dimensions.
var customDimensions = {
  SERVICE_WORKER_STATUS: 'dimension1'
};

// Creates the tracker object.
ga('create', 'UA-XXXXX-Y', 'auto');

// Sets the service worker status on the tracker,
// so its value is included in all future hits.
ga('set', customDimensions.SERVICE_WORKER_STATUS, getServiceWorkerStatus());

// Postpones sending any hits until after the page has fully loaded.
// This prevents analytics requests from delaying the loading of the page.
window.addEventListener('load', function() {
  // Sends a pageview for the initial pageload.
  ga('send', 'pageview');

  // Sends an event with the time to first paint data.
  sendTimeToFirstPaint();
});

Jak już wspomniałem, wysyłanie wymiaru Stan pracownika usługi z każdym odwołaniem umożliwia nam jego używanie w raportach o dowolnych danych.

Jak widać, prawie 85% wszystkich wyświetleń strony w przypadku IOWA pochodziło z przeglądarek, które obsługują skrypt service worker.

Wyniki: odpowiedzi na nasze pytania

Gdy zaczęliśmy zbierać dane, aby uzyskać odpowiedzi na nasze pytania, mogliśmy tworzyć raporty na ich podstawie. (Uwaga: wszystkie dane Google Analytics przedstawione tutaj odzwierciedlają rzeczywisty ruch w witrynie IOWA w okresie od 16 do 22 maja 2016 r.)

Pierwsze pytanie, jakie sobie zadaliśmy, brzmiało: Czy buforowanie w ramach usługi workera jest wydajniejsze niż istniejące mechanizmy buforowania HTTP dostępne we wszystkich przeglądarkach?

Aby odpowiedzieć na to pytanie, utworzyliśmy raport niestandardowy, w którym uwzględniliśmy dane Średni czas wczytywania strony w różnych wymiarach. Te dane są odpowiednie do udzielenia odpowiedzi na to pytanie, ponieważ zdarzenie load jest wywoływane dopiero po pobraniu wszystkich początkowych zasobów. W ten sposób odzwierciedla on bezpośrednio łączny czas wczytywania wszystkich kluczowych zasobów witryny5.

Wybrane przez nas wymiary to:

  • Nasz niestandardowy wymiar Stan skryptu Service Worker.
  • Typ użytkownika, który wskazuje, czy jest to pierwsza wizyta użytkownika w witrynie, czy powraca on do niej. (Uwaga: nowy użytkownik nie będzie miał żadnych zasobów w pamięci podręcznej, ale powracający użytkownik może mieć).
  • Kategoria urządzenia, która umożliwia porównanie wyników na urządzeniach mobilnych i komputerach.

Aby wykluczyć możliwość, że wyniki dotyczące czasu wczytywania są zafałszowane przez czynniki inne niż związane ze skryptem usługi, ograniczyliśmy nasze zapytanie do przeglądarek obsługujących skrypt usługi.

Jak widać, wizyty w naszej aplikacji, gdy były kontrolowane przez skrypt service worker, wczytywały się znacznie szybciej niż wizyty bez kontroli, nawet te od powracających użytkowników, którzy prawdopodobnie mieli większość zasobów strony w pamięci podręcznej. Warto też zauważyć, że w przypadku użytkowników mobilnych z usługą workera ładowanie było średnio szybsze niż w przypadku nowych użytkowników na komputerach.

„…visits to our app when controlled by a service worker loaded quite a bit faster than non-controlled visits…"

Więcej informacji znajdziesz w tych 2 tabelach:

Śr. czas wczytywania strony (komputera)
Stan skryptu Service Worker Typ użytkownika Śr. czas wczytywania strony (ms) Wielkość próbki
Sterowano Powracający użytkownik 2568 30860
Obsługiwane Powracający użytkownik 3612 1289
Obsługiwane Nowy użytkownik 4664 21991
Śr. czas wczytywania strony (mobilne)
Stan skryptu Service Worker Typ użytkownika Śr. czas wczytywania strony (ms) Wielkość próbki
Sterowano Powracający użytkownik 3760 8162
Obsługiwane Powracający użytkownik 4843 676
Obsługiwane Nowy użytkownik 6158 5779

Zastanawiasz się pewnie, jak to możliwe, że powracający użytkownik, którego przeglądarka obsługuje skrypt service worker, może znajdować się w niekontrolowanym stanie. Oto kilka możliwych wyjaśnień:

  • Użytkownik opuścił stronę podczas pierwszej wizyty, zanim skrypt service worker zdążył się zainicjować.
  • Użytkownik odinstalował service workera za pomocą narzędzi dla deweloperów.

Obie te sytuacje są stosunkowo rzadkie. Widać to w danych w kolumnie Próbka wczytania strony. Zwróć uwagę, że środkowe wiersze mają znacznie mniejszą próbkę niż pozostałe 2 wiersze.

Drugie pytanie brzmiało: Jak usługa typu service worker wpływa na wczytywanie witryny?

Aby uzyskać odpowiedź na to pytanie, utworzyliśmy kolejny raport niestandardowy z danymi Śr. wartość zdarzenia i odfiltrowaliśmy wyniki tak, aby uwzględniały tylko zdarzenia firstpaint. Użyliśmy wymiarów Kategoria urządzenia i naszego wymiaru niestandardowego Stan pracownika usługi.

Wbrew moim oczekiwaniom, usługa wtyczki na urządzeniach mobilnych miała znacznie mniejszy wpływ na czas do pierwszego wyświetlenia niż na ogólne wczytywanie strony.

„…na urządzeniach mobilnych usługa w tle miała znacznie mniejszy wpływ na czas do pierwszego wyświetlenia niż na ogólny czas wczytywania strony”.

Aby to sprawdzić, musimy dokładniej przeanalizować dane. Średnie mogą być przydatne do ogólnego przeglądu i oceny ogólnej sytuacji, ale aby naprawdę zrozumieć, jak te liczby rozkładają się na różnych użytkowników, musimy przyjrzeć się rozkładowi firstpaint.

Uzyskiwanie rozkładu danych w Google Analytics

Aby uzyskać rozkład wartości firstpaint, potrzebujemy dostępu do poszczególnych wyników każdego zdarzenia. Niestety Google Analytics nie ułatwia tego zadania.

Google Analytics umożliwia podział raportu według dowolnego wymiaru, ale nie według danych. Nie oznacza to, że jest to niemożliwe. Po prostu musieliśmy nieco dostosować naszą implementację, aby uzyskać pożądany wynik.

Wyniki raportu można rozbicie tylko według wymiarów, więc musieliśmy ustawić wartość danych (w tym przypadku firstpaint czas) jako wymiar niestandardowy zdarzenia. W tym celu utworzyliśmy kolejny wymiar niestandardowy o nazwie Wartość danych i zmieniliśmy logikę firstpaint śledzenia w ten sposób:

var customDimensions = {
  SERVICE_WORKER_STATUS: 'dimension1',
  <strong>METRIC_VALUE: 'dimension2'</strong>
};

// ...

function sendTimeToFirstPaint() {
  var timeToFirstPaint = getTimeToFirstPaintIfSupported();

  if (timeToFirstPaint) {
    var fields = {
      eventCategory: 'Performance',
      eventAction: 'firstpaint',
      // Rounds to the nearest millisecond since
      // event values in Google Analytics must be integers.
      eventValue: Math.round(timeToFirstPaint)
      // Sends this as a non-interaction event,
      // so it doesn't affect bounce rate.
      nonInteraction: true
    }

    <strong>// Sets the event value as a dimension to allow for breaking down the
    // results by individual metric values at reporting time.
    fields[customDimensions.METRIC_VALUE] = String(fields.eventValue);</strong>

    ga('send', 'event', fields);
  }
}

Interfejs internetowy Google Analytics nie umożliwia obecnie wizualizacji rozkładu dowolnych wartości danych, ale za pomocą interfejsu Google Analytics Core Reporting APIbiblioteki Google Charts mogliśmy wysłać zapytanie o nieprzetworzone wyniki, a potem sami utworzyć histogram.

Aby uzyskać rozkład wartości firstpaint na komputerach z systemem Windows przy użyciu niekontrolowanego service workera, zastosowano na przykład tę konfigurację żądania interfejsu API:

{
  dateRanges: [{startDate: '2016-05-16', endDate: '2016-05-22'}],
  metrics: [{expression: 'ga:totalEvents'}],
  dimensions: [{name: 'ga:dimension2'}],
  dimensionFilterClauses: [
    {
      operator: 'AND',
      filters: [
        {
          dimensionName: 'ga:eventAction',
          operator: 'EXACT',
          expressions: ['firstpaint']
        },
        {
          dimensionName: 'ga:dimension1',
          operator: 'EXACT',
          expressions: ['supported']
        },
        {
          dimensionName: 'ga:deviceCategory',
          operator: 'EXACT',
          expressions: ['desktop']
        }
      ],
    }
  ],
  orderBys: [
    {
      fieldName: 'ga:dimension2',
      orderType: 'DIMENSION_AS_INTEGER'
    }
  ]
}

To żądanie interfejsu API zwraca tablicę wartości wyglądającą tak (uwaga: to tylko pierwsze 5 wyników): Wyniki są sortowane od najmniejszego do największego, więc te wiersze reprezentują najkrótsze czasy.

Wyniki odpowiedzi interfejsu API (pierwsze 5 wierszy)
ga:dimension2 ga:totalEvents
4 3
5 2
6 10
7 8
8 10

Oto, co oznaczają te wyniki:

  • W 3 przypadkach wartość firstpaint wynosiła 4 ms.
  • Wystąpiły 2 zdarzenia, w których wartość firstpaint wynosiła 5 ms
  • Wystąpiło 10 zdarzeń, w których przypadku wartość firstpaint wynosiła 6 ms
  • Wystąpiło 8 zdarzeń, w których przypadku wartość firstpaint wynosiła 7 ms
  • Wystąpiło 10 zdarzeń, w których przypadku wartość firstpaint value wynosiła 8 ms
  • itd.

Na podstawie tych wyników możemy ekstrapolować wartość firstpaint dla każdego zdarzenia i utworzyć histogram rozkładu. Zrobiliśmy to w przypadku każdego z wykonanych zapytań.

Tak wyglądała dystrybucja na komputerach z niekontrolowanym (ale obsługiwanym) skryptem service worker:

Rozkład czasu do pierwszego wyrenderowania na komputerach (obsługiwany)

Średni czas firstpaint w przypadku tej dystrybucji to 912 ms.

Kształt tej krzywej jest dość typowy dla rozkładu czasu wczytywania. Porównaj to z histogramem poniżej, który pokazuje rozkład zdarzeń pierwszego wyświetlenia w przypadku wizyt, w których sterowanie stroną przejął pracownik usługi.

Rozkład czasu pierwszego wyrenderowania na komputerach (kontrolowane)

Zwróć uwagę, że gdy skrypt service worker kontrolował stronę, wielu użytkowników miało niemal natychmiastowy pierwszy render, a średni czas wynosił 583 ms.

„…gdy skrypt service worker kontrolował stronę, wielu użytkowników miało niemal natychmiastowe wyświetlenie pierwszego rysunku…”

Aby lepiej zrozumieć, jak te 2 rozkłady się od siebie różnią, na następnym wykresie widać je złączone. Histogram pokazujący niekontrolowane wizyty robota usługowego jest nałożony na histogram pokazujący kontrolowane wizyty, a oba są nałożone na histogram pokazujący obie te grupy.

Rozkład czasu do pierwszego wyrenderowania na komputerach

Ciekawym wynikiem było to, że rozkład z kontrolowanym pracownikiem usługi nadal miał krzywą w kształcie dzwonu po początkowym wzroście. Spodziewałem się dużego początkowego skoku, a potem stopniowego wygaszania, ale nie spodziewałem się drugiego szczytu na krzywej.

Gdy zaczęłam szukać przyczyny tego problemu, okazało się, że mimo że skrypt service worker może kontrolować stronę, jego wątek może być nieaktywny. Przeglądarka robi to, aby oszczędzać zasoby – nie musisz przecież mieć aktywnych i gotowych do działania wszystkich usług w przypadku każdej odwiedzonej przez Ciebie witryny. To wyjaśnia ogon rozkładu. W przypadku niektórych użytkowników wystąpiło opóźnienie podczas uruchamiania wątku usługi.

Jak widać na wykresie rozkładu, nawet przy tym początkowym opóźnieniu przeglądarki z usługą workera dostarczały treści szybciej niż przeglądarki korzystające z sieci.

Oto jak to wyglądało na urządzeniu mobilnym:

Rozkład czasu do pierwszego wyrenderowania na urządzeniach mobilnych

Wciąż odnotowaliśmy znaczny wzrost czasu pierwszego wyrenderowania treści, ale ogon był nieco dłuższy i szerszy. Prawdopodobnie dlatego, że na urządzeniu mobilnym uruchomienie wątku nieaktywnego service workera zajmuje więcej czasu niż na komputerze. To też wyjaśnia, dlaczego różnica między średnim czasem firstpaint nie była tak duża, jak się spodziewałem (omówiona powyżej).

„…na urządzeniach mobilnych uruchamianie wątku nieaktywnego service workera trwa dłużej niż na komputerach”.

Oto podział tych wariantów średniego czasu pierwszego wyświetlenia na urządzeniach mobilnych i na komputerach pogrupowanych według stanu usługi:

Mediana czasu do pierwszego wyrenderowania (ms)
Stan skryptu Service Worker Komputer Urządzenia mobilne
Sterowano 583 1634
Obsługiwane (niekontrolowane) 912 1933

Tworzenie tych wizualizacji rozkładu zajęło nam nieco więcej czasu i wysiłku niż tworzenie raportu niestandardowego w Google Analytics, ale dzięki nim znacznie lepiej rozumiemy, jak service workerzy wpływają na wydajność naszej witryny.

Inne skutki skryptów service worker

Oprócz wpływu na wydajność, serwisy workerów wpływają na komfort użytkownika w kilka innych sposobów, które można mierzyć za pomocą Google Analytics.

Dostęp offline

Skrypty service worker umożliwiają użytkownikom interakcję z Twoją witryną w trybie offline. Chociaż w przypadku każdej progresywnej aplikacji internetowej jakikolwiek rodzaj obsługi offline jest prawdopodobnie kluczowy, to określenie, jak bardzo jest on kluczowy w Twoim przypadku, zależy głównie od tego, jak często użytkownicy korzystają z Twojej witryny w trybie offline. Jak to jednak mierzyć?

Wysyłanie danych do Google Analytics wymaga połączenia z internetem, ale nie wymaga, aby dane były wysyłane dokładnie w momencie interakcji. Google Analytics obsługuje wysyłanie danych interakcji po fakcie przez określenie przesunięcia czasowego (za pomocą parametru qt).

Przez ostatnie 2 lata IOWA używała skryptu usługi, który wykrywa nieudane wywołania Google Analytics, gdy użytkownik jest offline, i odtwarza je później za pomocą parametru qt.

Aby śledzić, czy użytkownik jest online czy offline, utworzyliśmy wymiar niestandardowy o nazwie Online i ustaliliśmy jego wartość na navigator.onLine. Następnie nasłuchiwaliśmy zdarzeń onlineoffline, aby odpowiednio zaktualizować wymiar.

Aby dowiedzieć się, jak często użytkownicy korzystają z IOWA offline, utworzyliśmy segment, który obejmował użytkowników z co najmniej jedną interakcją offline. Okazało się, że to prawie 5% użytkowników.

Powiadomienia push

Usługa workera umożliwia użytkownikom zgłaszanie chęci otrzymywania powiadomień push. W IOWA użytkownicy byli powiadamiani, gdy miała się rozpocząć sesja na ich harmonogramie.

Podobnie jak w przypadku innych form powiadomień, ważne jest, aby znaleźć równowagę między dostarczaniem wartości użytkownikowi a drażnieniem go. Aby lepiej zrozumieć, co się dzieje, warto śledzić, czy użytkownicy zgadzają się na otrzymywanie tych powiadomień, czy korzystają z tych powiadomień, gdy je otrzymują, oraz czy użytkownicy, którzy wcześniej wyrazili zgodę, zmieniają swoje ustawienia i wyrażają sprzeciw.

W IOWA wysyłaliśmy tylko powiadomienia związane z spersonalizowanym harmonogramem użytkownika, który mogli tworzyć tylko zalogowani użytkownicy. Ograniczyło to zestaw użytkowników, którzy mogli otrzymywać powiadomienia, do zalogowanych użytkowników (śledzonych za pomocą wymiaru niestandardowego o nazwie Zalogowany), których przeglądarki obsługiwały powiadomienia push (śledzone za pomocą innego wymiaru niestandardowego o nazwie Uprawnienia dotyczące powiadomień).

Ten raport jest oparty na danych Użytkownicy i wymiarze niestandardowym Powiadomienia push, a segmentowany według użytkowników, którzy w danym momencie byli zalogowani, a ich przeglądarki obsługują powiadomienia push.

Cieszymy się, że ponad połowa zalogowanych użytkowników zdecydowała się na otrzymywanie powiadomień push.

Banery promujące instalację aplikacji

Jeśli progresywna aplikacja internetowa spełnia kryteria i jest często używana przez użytkownika, może wyświetlić mu się baner z prośbą o dodanie aplikacji do ekranu głównego.

W IOWA śledziłyśmy, jak często te prompty były wyświetlane użytkownikom (i czy zostały zaakceptowane) za pomocą tego kodu:

window.addEventListener('beforeinstallprompt', function(event) {
  // Tracks that the user saw a prompt.
  ga('send', 'event', {
    eventCategory: 'installprompt',
    eventAction: 'fired'
  });

  event.userChoice.then(function(choiceResult) {
    // Tracks the users choice.
    ga('send', 'event', {
      eventCategory: 'installprompt',
      // `choiceResult.outcome` will be 'accepted' or 'dismissed'.
      eventAction: choiceResult.outcome,
      // `choiceResult.platform` will be 'web' or 'android' if the prompt was
      // accepted, or '' if the prompt was dismissed.
      eventLabel: choiceResult.platform
    });
  });
});

Spośród użytkowników, którzy widzieli baner z prośbą o instalację aplikacji, około 10% z nich zdecydowało się dodać ją do ekranu głównego.

Możliwe ulepszenia śledzenia (na przyszłość)

Dane analityczne zebrane przez nas w tym roku w ramach IOWA były bezcenne. Ale wsteczna analiza zawsze pozwala dostrzec luki i możliwości poprawy na przyszłość. Po zakończeniu tegorocznej analizy chciałbym podzielić się 2 rzecząmi, które chcielibyśmy zrobić inaczej. Mogą one być przydatne dla czytelników, którzy chcą wdrożyć podobną strategię:

1. śledzić więcej zdarzeń związanych z obciążeniem,

Śledziłyśmy kilka zdarzeń odpowiadających metrykom technicznym (np. HTMLImportsLoaded, WebComponentsReady itp.), ale ponieważ większość wczytywania była wykonywana asynchronicznie, moment wystąpienia tych zdarzeń niekoniecznie odpowiadał konkretnemu momentowi w całym procesie wczytywania.

Głównym zdarzeniem związanym z wczytywaniem, którego nie śledzimy (ale chcielibyśmy to robić), jest moment, w którym znika ekran powitalny i użytkownik może zobaczyć zawartość strony.

2. Przechowywanie identyfikatora klienta Analytics w IndexedDB

Domyślnie biblioteka analytics.js przechowuje pole identyfikatora klienta w plikach cookie przeglądarki. Niestety skrypty service worker nie mają dostępu do plików cookie.

Wystąpił z tego powodu problem, gdy próbowaliśmy wdrożyć śledzenie powiadomień. Za każdym razem, gdy wysyłano powiadomienie do użytkownika, chcieliśmy wysłać zdarzenie z serwisowego workera (za pomocą protokołu Measurement Protocol), a potem śledzić skuteczność tego powiadomienia w przypadku ponownego zaangażowania, jeśli użytkownik kliknął powiadomienie i wrócił do aplikacji.

Udało nam się śledzić skuteczność powiadomień w ogóle za pomocą parametru utm_source campaign, ale nie udało się nam powiązać konkretnej sesji ponownego zaangażowania z konkretnym użytkownikiem.

Aby obejść to ograniczenie, można było zapisać identyfikator klienta w naszym kodzie śledzenia za pomocą IndexedDB, a następnie udostępnić tę wartość skryptowi usługi.

3. Zezwalanie skryptu service worker na raportowanie stanu online/offline

Sprawdzenie wartości navigator.onLine pozwoli Ci się dowiedzieć, czy przeglądarka może połączyć się z routerem lub siecią lokalną, ale niekoniecznie określi, czy użytkownik ma rzeczywiste połączenie. Ponieważ nasz skrypt usługi w ramach usługi analitycznej offline po prostu powtarzał nieudane uderzenia (bez ich modyfikowania ani oznaczania jako nieudanych), prawdopodobnie zaniżaliśmy liczbę użytkowników korzystających z funkcji offline.

W przyszłości powinniśmy śledzić zarówno stan navigator.onLine, jak i to, czy skrypt service worker odtworzył uderzenie z powodu początkowego błędu sieci. Dzięki temu uzyskamy dokładniejszy obraz rzeczywistego korzystania z aplikacji offline.

Podsumowanie

To badanie pokazuje, że korzystanie z usług workera rzeczywiście poprawia wydajność wczytywania aplikacji internetowej Google I/O w różnych przeglądarkach, sieciach i na różnych urządzeniach. Pokazują one też, że analiza rozkładu danych o obciążeniu w różnych przeglądarkach, sieciach i na różnych urządzeniach pozwala lepiej zrozumieć, jak ta technologia radzi sobie w rzeczywistych scenariuszach, i odkrywa nieoczekiwane cechy wydajności.

Oto najważniejsze wnioski z badania IOWA:

  • Średnio strony wczytywały się znacznie szybciej, gdy sterował nimi skrypt service worker, niż bez niego, zarówno w przypadku nowych, jak i powracających użytkowników.
  • W przypadku wielu użytkowników wizyty na stronach kontrolowanych przez skrypt service worker wczytują się niemal natychmiast.
  • W przypadku nieaktywnych usług roboczych uruchomienie może trochę potrwać. Nieaktywny skrypt service worker działał jednak lepiej niż brak skryptu.
  • Czas uruchamiania nieaktywnego skryptu service worker był dłuższy na urządzeniu mobilnym niż na komputerze.

Chociaż wzrost skuteczności w jednej konkretnej aplikacji jest przydatny do raportowania większej społeczności deweloperów, należy pamiętać, że te wyniki są specyficzne dla typu witryny IOWA (strona z informacjami o imprezie) i typu odbiorców (głównie deweloperzy).

Jeśli wdrażasz w aplikacji skrypt service worker, ważne jest, aby zastosować własną strategię pomiarową, która pozwoli Ci ocenić wydajność i zapobiegać przyszłym regresjom. Jeśli tak, udostępnij wyniki, aby wszyscy mogli z nich skorzystać.

Przypisy

  1. Nie jest do końca sprawiedliwe porównywać wydajność implementacji pamięci podręcznej serwisu workera z wydajnością naszej witryny z samą pamięcią podręczną HTTP. Ponieważ optymalizowaliśmy IOWA pod kątem usługi workera, nie poświęciliśmy zbyt wiele czasu na optymalizację pod kątem pamięci podręcznej HTTP. Gdyby tak było, wyniki pewnie byłyby inne. Więcej informacji o optymalizacji witryny pod kątem pamięci podręcznej HTTP znajdziesz w artykule Efektywne optymalizowanie treści.
  2. W zależności od tego, jak Twoja witryna wczytuje style i treści, przeglądarka może być w stanie wyświetlić obraz przed udostępnieniem treści lub stylów. W takich przypadkach firstpaint może odpowiadać pustemu białemu ekranowi. Jeśli używasz parametru firstpaint, musisz się upewnić, że odpowiada on istotnemu punktowi wczytywania zasobów witryny.
  3. Teoretycznie moglibyśmy wysłać metrykę timing (która domyślnie nie jest interakcją), aby zarejestrować te informacje zamiast zdarzenia. Do Google Analytics dodano uchwyty czasu właśnie po to, aby można było śledzić takie dane o obciążeniu. Jednak uchwyty czasu są mocno próbkowane w czasie przetwarzania, a ich wartości nie można używać w segmentach. Ze względu na te ograniczenia zdarzenia bez interakcji są nadal lepszym rozwiązaniem.
  4. Aby lepiej zrozumieć, jaki zakres powinien mieć wymiar niestandardowy w Google Analytics, zapoznaj się z sekcją Wymiar niestandardowy w Centrum pomocy Analytics. Ważne jest też zrozumienie modelu danych Google Analytics, który składa się z użytkowników, sesji i interakcji (trafień). Aby dowiedzieć się więcej, obejrzyj lekcję Akademii Analytics na temat modelu danych Google Analytics.
  5. Nie uwzględnia on zasobów wczytywanych z opóźnieniem po zdarzeniu wczytywania.