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

Jedną z najważniejszych zalet działów service worker (przynajmniej z perspektywy wydajności) jest ich możliwość proaktywnej kontroli buforowania zasobów. Aplikacja internetowa, która może buforować wszystkie potrzebne zasoby, powinna ładować się znacznie szybciej u powracających użytkowników. Ale jak te korzyści faktycznie wyglądają dla prawdziwych użytkowników? A jak to w ogóle mierzy?

Aplikacja internetowa Google I/O to progresywna aplikacja internetowa, która wykorzystuje większość nowych funkcji oferowanych przez pracowników usługowych, aby zapewniać użytkownikom zaawansowane funkcje podobne do aplikacji. Firma skorzystała również z Google Analytics, aby zebrać kluczowe dane dotyczące skuteczności i wzorców użytkowania wśród dużej i zróżnicowanej grupy użytkowników.

To studium przypadku pokazuje, jak firma IOWA wykorzystała Google Analytics, aby uzyskać odpowiedzi na kluczowe pytania dotyczące wydajności i opracować raport na temat rzeczywistego wpływu pracowników Service Worker.

Zacznij od pytań

Za każdym razem, gdy wdrażasz analitykę w witrynie lub aplikacji, ważne jest, aby zacząć od określenia pytań, na które starasz się odpowiedzieć na podstawie danych, które będziesz zbierać.

Chcieliśmy odpowiedzieć na kilka pytań, jednak na potrzeby tego studium przypadku skoncentrujmy się na dwóch z tych najciekawszych.

1. Czy pamięć podręczna mechanizmu Service Worker jest bardziej wydajna niż istniejące mechanizmy buforowania HTTP dostępne we wszystkich przeglądarkach?

Spodziewamy się, że strony będą wczytywały się szybciej u powracających użytkowników niż w przypadku nowych, ponieważ przeglądarki mogą buforować żądania i natychmiast wyświetlać je przy kolejnych wizytach.

Skrypty service worker udostępniają alternatywne funkcje buforowania, które dają programistom szczegółową kontrolę nad tym, co i w jaki sposób odbywa się w pamięci podręcznej. W IOWA zoptymalizowaliśmy implementację mechanizmu Service Worker, tak aby każdy zasób był buforowany, dzięki czemu powracający użytkownicy mogli korzystać z aplikacji całkowicie offline.

Czy jednak nie byłoby to lepsze niż to, co przeglądarka robi domyślnie? A jeśli tak, to o ile lepiej? 1

2. Jak skrypt service worker wpływa na wczytywanie strony?

Innymi słowy, jak szybko odczuwa ona czas wczytywania strony, niezależnie od rzeczywistego czasu wczytywania mierzonego przez tradycyjne dane dotyczące wczytywania strony.

Odpowiedź na pytania o odczucia nie jest łatwym zadaniem, a żadne dane nie oddadzą w pełni subiektywnego odczucia. Jednak niektóre dane bez wątpienia są lepsze od innych, dlatego trzeba wybrać właściwe dane.

Wybór odpowiednich danych

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

Śr. Czas wczytywania strony jest dobrym wskaźnikiem odpowiedzi na pierwsze pytanie, ale nie jest szczególnie dobrym wskaźnikiem, jeśli chodzi o uzyskanie odpowiedzi na drugie. Po pierwsze zdarzenie load nie musi odpowiadać momentowi, w którym użytkownik może faktycznie korzystać z aplikacji. Co więcej, w przypadku 2 aplikacji o takim samym czasie wczytywania może odnosić się do nich, że ładują się znacznie inaczej. Na przykład witryna z ekranem powitalnym lub wskaźnikiem wczytywania prawdopodobnie ładuje się znacznie szybciej niż witryna, która tylko przez kilka sekund wyświetla pustą stronę.

W programie Iowa zaobserwowaliśmy animację odliczania na ekranie powitalnym, która (według mnie) była bardzo skuteczna, aby zapewnić użytkownikowi rozrywkę, podczas gdy reszta aplikacji ładowała się w tle. Z tego względu śledzenie czasu potrzebnego na pojawienie się ekranu powitalnego ma znacznie bardziej sensowne narzędzie do mierzenia przewidywanej wydajności wczytywania. Aby uzyskać tę wartość, wybraliśmy dane czas do pierwszego wyrenderowania.

Gdy już określiliśmy pytania, na które chcemy odpowiedzieć, i określiliśmy dane, które pomogły nam w uzyskaniu odpowiedzi, przyszła pora na wdrożenie Google Analytics i rozpoczęcie pomiarów.

Implementacja analityki

Jeśli korzystasz już z Google Analytics, zapewne 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 tego kodu inicjuje globalną funkcję ga() (jeśli jeszcze nie istnieje), a ostatni wiersz asynchronicznie pobiera bibliotekę analytics.js.

Środkowa część zawiera następujące dwa wiersze:

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

Te 2 polecenia śledzą, jakie strony odwiedzają użytkownicy Twojej witryny, ale niewiele więcej. Jeśli chcesz śledzić dodatkowe interakcje użytkowników, musisz zrobić to samodzielnie.

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

  • Czas, który upływa od pierwszego rozpoczęcia wczytywania strony do wyświetlenia pikseli na ekranie.
  • Wskazuje, czy skryptem service worker kontroluje stronę. Dysponując tymi informacjami, możemy posegmentować raporty, aby porównać wyniki z użyciem skryptu service worker i bez niego.

Czas do pierwszego wyrenderowania

Niektóre przeglądarki rejestrują dokładny czas wyrenderowania pierwszego piksela na ekranie i udostępniają ten czas deweloperom. Ta wartość w porównaniu z wartością navigationStart wyświetlaną przez Navigation Timing API pozwala nam bardzo dokładnie sprawdzić, ile czasu upłynęło między początkowym żądaniem wyświetlenia strony przez użytkownika a jego wyświetleniem.

Jak już wspomnieliśmy, czas do pierwszego wyrenderowania jest ważnym wskaźnikiem, który należy mierzyć, ponieważ jest to pierwszy punkt, w którym użytkownik doświadcza szybkości wczytywania witryny. To pierwsze wrażenie, jakie widzą użytkownicy, a dobre pierwsze wrażenie może wpłynąć na ich zadowolenie2.

Aby uzyskać pierwszą wartość wyrenderowania w przeglądarkach, które ją ujawniają, stworzyliśmy funkcję użytkową 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 niewymagające interakcji z czasem do pierwszego wyrenderowania jako jego wartością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 następująco:

// 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 powyżej działa kod, piksele mogły zostać wcześniej wyrenderowane na ekranie. Aby mieć pewność, że ten kod będzie zawsze uruchamiany po pierwszym wyrenderowaniu, przesunęliśmy wywołanie do sendTimeToFirstPaint() na zdarzenie load. Postanowiliśmy odłożyć wysyłanie wszystkich danych analitycznych do czasu załadowania strony, aby mieć pewność, że żądania te nie będą konkurować z ładowaniem 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();
});

Powyższy kod zgłasza firstpaint razy do Google Analytics, ale to tylko połowa sukcesu. Musieliśmy śledzić stan skryptu service worker. W przeciwnym razie nie moglibyśmy porównać pierwszych czasów renderowania strony kontrolowanej przez skrypt service worker i strony niekontrolowanej.

Określanie stanu skryptu service worker

Aby określić bieżący stan skryptu service worker, utworzyliśmy funkcję użytkową, która zwraca jedną z 3 wartości:

  • kontrolowany – skrypt service worker kontroluje stronę. W przypadku IOWA oznacza to, że wszystkie zasoby zostały zapisane w pamięci podręcznej i strona działa offline.
  • supported: przeglądarka obsługuje skrypt service worker, ale ten skrypt nie kontroluje jeszcze strony. To jest oczekiwany stan w przypadku użytkowników odwiedzających witrynę po raz pierwszy.
  • unsupported (nieobsługiwane): przeglądarka użytkownika nie obsługuje skryptu service worker.
function getServiceWorkerStatus() {
  if ('serviceWorker' in navigator) {
    return navigator.serviceWorker.controller ? 'controlled' : 'supported';
  } else {
    return 'unsupported';
  }
}

Ta funkcja otrzymała dla nas stan skryptu service worker; kolejnym krokiem było powiązanie tego stanu z danymi, które przesyłaliśmy do Google Analytics.

Śledzenie danych niestandardowych za pomocą wymiarów niestandardowych

Domyślnie Google Analytics oferuje wiele sposobów na dzielenie całego ruchu na grupy według atrybutów użytkownika, sesji lub interakcji. Są to tzw. wymiary. Typowe wymiary, którymi zajmują się internetowe deweloperzy, to Przeglądarka, System operacyjny i Kategoria urządzeń.

Stan skryptu service worker nie jest wymiarem udostępnianym domyślnie przez Google Analytics. Google Analytics umożliwia jednak tworzenie własnych wymiarów niestandardowych i definiowanie ich w dowolny sposób.

W przypadku IOWA utworzyliśmy niestandardowy wymiar o nazwie Stan skryptu Service i ustawić jego zakres na działanie (tj. pojedynczą interakcję).4 Każdy wymiar niestandardowy, który tworzysz w Google Analytics, otrzymuje unikalny indeks w tej usłudze, a w Twoim kodzie śledzenia możesz się odwoływać do tego wymiaru za pomocą jego indeksu. Jeśli np. indeks nowo utworzonego wymiaru wynosi 1, możemy zaktualizować funkcje logiczne w ten sposób, aby wysyłać zdarzenie firstpaint zawierające stan skryptu service worker:

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 powoduje powiązanie tylko stanu skryptu service worker z tym konkretnym zdarzeniem. Ze względu na to, że stan skryptu service worker może się przydać przy każdej interakcji, najlepiej podawać go we wszystkich danych wysyłanych do Google Analytics.

Aby informacje te były uwzględniane we wszystkich działaniach (np. wszystkich odsłonach, zdarzeniach itp.), przed wysłaniem danych do Google Analytics ustawiamy wartość wymiaru niestandardowego w samym obiekcie tracker.

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

Po ustawieniu tej wartości będzie ona wysyłana ze wszystkimi kolejnymi działaniami w przypadku bieżącego wczytywania strony. Jeśli użytkownik wczyta stronę ponownie później, funkcja getServiceWorkerStatus() prawdopodobnie zwróci nową wartość, która zostanie ustawiona w obiekcie skryptu śledzenia.

Krótka uwaga na temat przejrzystości i czytelności kodu: inne osoby przeglądające ten kod mogą nie wiedzieć, do czego odnosi się dimension1, więc zawsze najlepiej utworzyć zmienną, która mapuje zrozumiałe 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ż wspominaliśmy, wysłanie wymiaru Stan skryptu Service Worker z każdym działaniem umożliwia nam korzystanie z niego w raportach o dowolnych danych.

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

Odpowiedzi na pytania

Gdy zaczęliśmy zbierać dane w odpowiedzi na nasze pytania, mogliśmy je wykorzystać w raportach, aby zobaczyć wyniki. (Uwaga: wszystkie widoczne tu dane Google Analytics odzwierciedlają rzeczywisty ruch na stronie IOWA 2016 r. w okresie 16–22 maja 2016 r.).

Pierwsze pytanie brzmiało: Czy buforowanie skryptu service worker jest bardziej wydajne niż istniejące mechanizmy buforowania HTTP dostępne we wszystkich przeglądarkach?

Aby odpowiedzieć na to pytanie, utworzyliśmy raport niestandardowy, który uwzględniał dane Śr. Czasy wczytywania stron w różnych wymiarach. Te dane dobrze nadają się do uzyskania odpowiedzi na to pytanie, ponieważ zdarzenie load jest uruchamiane dopiero po pobraniu wszystkich zasobów początkowych. Odzwierciedla więc bezpośrednio łączny czas wczytywania wszystkich kluczowych zasobów witryny5.

Wybraliśmy te wymiary:

  • Nasz niestandardowy wymiar Stan skryptu service worker.
  • Typ użytkownika, który wskazuje, czy jest to pierwsza wizyta użytkownika w witrynie, czy powraca. (Uwaga: nowy użytkownik nie będzie miał żadnych zasobów w pamięci podręcznej; może to być powracający użytkownik).
  • Kategoria urządzenia – pozwala nam porównywać wyniki na urządzeniach mobilnych i komputerach.

Aby uchronić się przed tym, że czynniki niezwiązane z skryptami service worker zniekształcały wyniki, ograniczyliśmy zapytanie tak, aby obejmowało tylko przeglądarki obsługujące mechanizm Service Worker.

Jak widać, wizyty w naszej aplikacji kontrolowane przez mechanizm Service Worker ładowały się znacznie szybciej niż wizyty niekontrolowane, nawet jeśli większość zasobów strony znajdowała się w pamięci podręcznej. Warto też zauważyć, że użytkownicy korzystający z komórek z obsługą skryptu wczytują się przeciętnie szybciej niż nowi użytkownicy komputerów.

„...wizyty w naszej aplikacji kontrolowane przez skrypt service worker ładowały się znacznie szybciej niż wizyty niekontrolowane...”

Więcej informacji znajdziesz w tych 2 tabelach:

Śr. Czas wczytywania strony (na komputerach)
Stan skryptu service worker Typ użytkownika Śr. czas wczytywania strony (ms) Rozmiar 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 (urządzenia mobilne)
Stan skryptu service worker Typ użytkownika Śr. czas wczytywania strony (ms) Rozmiar próbki
Sterowano Powracający użytkownik 3760 8162
Obsługiwane Powracający użytkownik 4843 676
Obsługiwane Nowy użytkownik 6158 5779

Być może zastanawiasz się, w jaki sposób powracający użytkownik, którego przeglądarka obsługuje mechanizm Service Worker, może znajdować się w niekontrolowanym stanie. Istnieje kilka możliwych przyczyn tej sytuacji:

  • Użytkownik opuścił stronę podczas pierwszej wizyty, zanim skrypt service worker miał szansę dokończyć inicjowanie.
  • Użytkownik odinstalował skrypt service worker za pomocą narzędzi dla programistów.

Obie te sytuacje występują dość rzadko. Widzimy to w danych, spoglądając na wartości parametru Page Load Sample (Próbka wczytywania strony) w czwartej kolumnie. Zwróć uwagę, że środkowe wiersze zawierają znacznie mniejszą próbkę niż pozostałe.

Nasze drugie pytanie brzmiało: Jak skrypt service worker wpływa na wczytywanie strony?

Aby odpowiedzieć na to pytanie, utworzyliśmy inny raport niestandardowy dla danych Śr. Wartość zdarzenia i przefiltrowaliśmy wyniki, aby uwzględnić tylko zdarzenia firstpaint. Użyliśmy wymiarów Kategoria urządzenia i nasz niestandardowy wymiar Stan usługi Service Worker.

Wbrew temu, czego można było oczekiwać, mechanizm Service Worker na urządzeniach mobilnych miał znacznie mniejszy wpływ na czas pierwszego wyrenderowania niż na ogólne wczytywanie strony.

„...serwis na urządzeniach mobilnych miał znacznie mniejszy wpływ na czas pierwszego wyrenderowania niż na ogólne wczytywanie strony”.

Aby sprawdzić, dlaczego tak jest, musimy dokładniej przyjrzeć się danym. Średnie mogą być dobre w przypadku ogólnych streszczeń i obszernych przekreśleń. Aby jednak zrozumieć, jak te liczby radzą sobie w przypadku różnych użytkowników, musimy spojrzeć na rozkład firstpaint razy.

Uzyskiwanie dystrybucji danych w Google Analytics

Aby uzyskać rozkład firstpaint razy, potrzebujemy dostępu do poszczególnych wyników dotyczących każdego wydarzenia. Google Analytics nie ułatwia tego zadania.

Google Analytics umożliwia podział raportu według dowolnych wymiarów, ale nie pozwala na podział raportu według danych. Nie oznacza to, że jest to niemożliwe, ale po prostu musieliśmy jeszcze bardziej dostosować implementację, aby uzyskać pożądany wynik.

Wyniki raportu można podzielić tylko według wymiarów, więc musieliśmy ustawić wartość danych (w tym przypadku firstpaint raz) jako wymiar niestandardowy dla zdarzenia. W tym celu utworzyliśmy kolejny wymiar niestandardowy o nazwie Wartość danych i zaktualizowaliśmy logikę śledzenia firstpaint w następujący 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 dzięki interfejsowi Google Analytics Core Reporting API i bibliotece wykresów Google mogliśmy wysyłać zapytania o nieprzetworzone wyniki, a następnie samodzielnie stworzyć histogram.

Na przykład poniższa konfiguracja żądania do interfejsu API została użyta do uzyskania rozkładu wartości firstpaint na komputerze z niekontrolowanym skryptem service worker.

{
  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 do interfejsu API zwraca tablicę wartości, które wyglądają jak poniżej (uwaga: to tylko pięć pierwszych wyników). Wyniki są sortowane od najmniejszej do największej wartości, więc w tych wierszach podane są najszybsze czasy.

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

Oto co oznaczają te wyniki w prostym języku angielskim:

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

Na podstawie tych wyników możemy ekstrapolować wartość firstpaint dla każdego zdarzenia i utworzyć histogram rozkładu. Robiliśmy to w przypadku każdego uruchamianego zapytania.

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

Czas do pierwszego wyrenderowania na komputerach (obsługiwany)

Mediana czasu firstpaint dla powyższego rozkładu wynosi 912 ms.

Kształt tej krzywej jest dość typowy dla rozkładu czasu wczytywania. Z kolei histogram poniżej pokazuje rozkład pierwszych zdarzeń wyrenderowania w przypadku wizyt, podczas których skrypt service worker kontroluje stronę.

Czas do pierwszego wyrenderowania na komputerach (kontrolowane)

Zauważ, że podczas kontrolowania strony przez skrypt service worker wielu użytkowników doświadczyło niemal natychmiastowego pierwszego wyrenderowania – mediana wartości to 583 ms.

„...gdy mechanizm Service Worker kontroluje stronę, wielu użytkowników doświadczyło niemal natychmiastowego pierwszego wyrenderowania...”

Aby lepiej porównać te 2 dystrybucje, na następnym wykresie znajdziesz ich widok połączony. Histogram pokazujący odwiedziny niekontrolowanego skryptu service worker jest nałożony na histogram przedstawiający kontrolowane odwiedziny, a oba te rodzaje wizyt są nałożone na histogram, który przedstawia obie te wartości.

Czas do pierwszego wyrenderowania na komputerach

W tych wynikach zaciekawiło mnie to, że po początkowym wzroście nadal wykazywała krzywa w kształcie dzwonnicy w przypadku kontrolowanego mechanizmu Service Worker. Spodziewałem się znacznego początkowego skoku, a potem stopniowego szlaku w końcu, nie spodziewałem się drugiego szczytu na krzywej.

Po sprawdzeniu, co może być przyczyną problemu, dowiedziałem się, że chociaż skrypt service worker może kontrolować stronę, jej wątek może być nieaktywny. Przeglądarka robi to, aby zaoszczędzić zasoby – oczywiście nie musisz korzystać z każdego skryptu service worker w każdej odwiedzonej witrynie, aby była aktywna i gotowa do działania. To wyjaśnia ogon rozkładu. W przypadku niektórych użytkowników wystąpiło opóźnienie podczas uruchamiania wątku skryptu service worker.

Jak widać jednak na podstawie dystrybucji, nawet przy tym początkowym opóźnieniu przeglądarki z skrypcją service worker dostarczały treści szybciej niż te, które korzystają z sieci.

Tak to wyglądało na komórce:

Czas do pierwszego wyrenderowania na urządzeniach mobilnych

Chociaż odnotowaliśmy znaczny wzrost niemal od razu po pierwszym wyrenderowaniu, ogon był nieco większy i dłuższy. Jest to prawdopodobnie spowodowane tym, że na urządzeniach mobilnych rozpoczęcie nieaktywnego wątku service worker trwa dłużej niż na komputerze. Wyjaśnia również, dlaczego różnica między średnim czasem firstpaint nie jest tak duża, jak się spodziewałam (opisywana powyżej).

„...na urządzeniach mobilnych uruchomienie wątku bezczynnego skryptu service worker trwa dłużej niż na komputerze”.

Oto podział tych odmian mediany czasu pierwszego renderowania na urządzeniach mobilnych i komputerach pogrupowane według stanu skryptu service worker:

Mediana czasu do pierwszego wyrenderowania (ms)
Stan skryptu service worker Komputer Urządzenia mobilne
Sterowano 583 1634
Obsługiwany (niekontrolowany) 912 1933

Chociaż tworzenie tych wizualizacji rozkładu zajęło trochę więcej czasu i wysiłku niż utworzenie niestandardowego raportu w Google Analytics, daje nam to o wiele lepsze pojęcie o tym, jak mechanizmy Service Worker wpływają na wydajność witryny.

Inny wpływ mechanizmów Service Worker

Skrypty service worker wpływają nie tylko na wydajność, ale też na wrażenia użytkowników na kilka innych sposobów, które można zmierzyć za pomocą Google Analytics.

Dostęp offline

Skrypty service worker pozwalają użytkownikom korzystać z witryny w trybie offline i chociaż jakiś rodzaj obsługi offline ma prawdopodobnie kluczowe znaczenie dla każdej progresywnej aplikacji internetowej, to określenie jego znaczenia w Twoim przypadku w dużym stopniu zależy od intensywności użytkowania offline. Ale jak to zmierzyć?

Przesyłanie danych do Google Analytics wymaga połączenia z internetem, ale nie musi być ono wysyłane dokładnie w momencie wystąpienia interakcji. Google Analytics obsługuje wysyłanie danych o interakcji później, określając przesunięcie czasu (za pomocą parametru qt).

Przez ostatnie 2 lata organizacja IOWA korzystała ze skryptu skryptu service worker, który wykrywa nieudane działania w Google Analytics, gdy użytkownik jest offline, i odtwarza je później z użyciem parametru qt.

Aby śledzić, czy użytkownik był online czy offline, utworzyliśmy wymiar niestandardowy o nazwie Online i ustawiliśmy go na wartość navigator.onLine. Następnie przesłuchaliśmy zdarzenia online i offline i odpowiednio go zaktualizowaliśmy.

Aby zorientować się, jak często użytkownicy byli offline podczas korzystania z systemu IOWA, utworzyliśmy segment, który kierował reklamy na użytkowników, którzy w czasie przynajmniej 1 interakcji offline korzystali z internetu. Jak się okazuje, było to prawie 5% użytkowników.

Powiadomienia push

Skrypty service worker pozwalają użytkownikom wyrazić zgodę na otrzymywanie powiadomień push. W IOWA użytkownicy byli powiadamiani o zbliżającej się sesji w harmonogramie.

Tak jak w przypadku każdej formy powiadomień, ważne jest znalezienie równowagi między dostarczaniem użytkownikom wartości a ich irytacją. Aby lepiej zrozumieć, co się dzieje, należy śledzić, czy użytkownicy wyrazili zgodę na otrzymywanie takich powiadomień, czy wchodzą z nimi w interakcję w momencie ich wyświetlenia oraz czy użytkownicy, którzy wcześniej wyrazili zgodę, zmienili swoje preferencje i zrezygnowali z tej funkcji.

W IOWA wysyłaliśmy tylko powiadomienia związane ze spersonalizowanym harmonogramem użytkownika. Tylko zalogowani użytkownicy mogli tworzyć takie treści. Ograniczało to grupę użytkowników, którzy mogą otrzymywać powiadomienia do zalogowanych użytkowników (śledzonych za pomocą wymiaru niestandardowego Zalogowani), których przeglądarki obsługiwały powiadomienia push (śledzone za pomocą innego wymiaru niestandardowego o nazwie Uprawnienia do powiadomień).

Poniższy raport jest oparty na danych Użytkownicy i wymiarze niestandardowym Uprawnienia do wyświetlania powiadomień podzielonych na segmenty według użytkowników, którzy się w pewnym momencie zalogowali, a ich przeglądarki obsługują powiadomienia push.

Cieszymy się, że ponad połowa zalogowanych użytkowników zdecydowała się otrzymywać powiadomienia push.

Banery promujące instalacje aplikacji

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

W IOWA śledziliśmy, jak często te komunikaty były wyświetlane użytkownikowi (i czy zostały zaakceptowane) za pomocą następującego 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
    });
  });
});

Około 10% użytkowników, którzy zobaczyli baner promujący instalację aplikacji, zdecydowało się dodać go do ekranu głównego.

Możliwe ulepszenia śledzenia (następnym razem)

Dane analityczne, które zebraliśmy w tym roku przez IOWA, były bezcenne. Jednak spojrzenie z przeszłości zawsze wiąże się z trudnościami i szansami na ulepszenie kolejnego podejścia. Po zakończeniu tegorocznej analizy chciałabym, aby czytelnicy, którzy chcą wdrożyć podobną strategię, mogliby wziąć pod uwagę 2 rzeczy:

1. Śledź więcej zdarzeń związanych z ładowaniem

Śledziliśmy kilka zdarzeń związanych z danymi technicznymi (np. HTMLImportsLoaded, WebComponentsReady itp.), ale ze względu na to, że duża część obciążenia była wykonywana asynchronicznie, moment wywołania tych zdarzeń nie musi odpowiadać konkretnemu momentowi w całym środowisku wczytywania.

Głównym zdarzeniem związanym z wczytywaniem, którego nie śledziliśmy (ale szkoda, że nie mieliśmy), jest moment, w którym ekran powitalny zniknął i użytkownik mógł zobaczyć zawartość strony.

2. Zapisywanie identyfikatora klienta Analytics w IndexedDB

Domyślnie kod analytics.js przechowuje pole identyfikatora klienta w plikach cookie przeglądarki. Skrypty mechanizmu Service Worker nie mają dostępu do plików cookie.

To stanowiło problem przy próbie wdrożenia śledzenia powiadomień. Chcieliśmy wysyłać zdarzenie z skryptu service worker (za pomocą Measurement Protocol) za każdym razem, gdy powiadomienie zostało wysłane do użytkownika, a następnie śledzić powodzenie tego powiadomienia w przypadku ponownego zaangażowania, jeśli użytkownik je kliknął i wrócił do aplikacji.

Skuteczność powiadomień mogliśmy śledzić za pomocą parametru kampanii utm_source, ale nie udało nam się powiązać konkretnej sesji ponownego zaangażowania z konkretnym użytkownikiem.

Mogliśmy obejść to ograniczenie, zapisując identyfikator klienta w IndexedDB w naszym kodzie śledzenia, dzięki czemu wartość ta byłaby dostępna dla skryptu service worker.

3. Zezwalaj skryptowi service worker na zgłaszanie stanu online/offline

Sprawdzenie navigator.onLine da Ci znać, czy Twoja przeglądarka może połączyć się z routerem lub siecią lokalną, ale niekoniecznie da Ci znać, czy użytkownik ma połączenie z siecią. A ponieważ nasz skrypt skryptu service worker offline po prostu ponownie odtwarzał nieudane działania (bez ich modyfikowania i oznaczania jako nieudane), prawdopodobnie zaniżaliśmy użycie danych w trybie offline.

W przyszłości powinniśmy śledzić zarówno stan obiektu navigator.onLine, jak i to, czy działanie zostało ponownie odtworzone przez mechanizm Service Worker z powodu początkowej awarii sieci. Daje nam to dokładniejszy obraz prawdziwego korzystania z internetu w trybie offline.

Podsumowanie

To studium przypadku pokazuje, że zastosowanie mechanizmu Service Worker rzeczywiście poprawiło wydajność wczytywania aplikacji internetowej Google I/O w wielu przeglądarkach, sieciach i na różnych urządzeniach. Po przeanalizowaniu rozkładu danych dotyczących obciążenia w szerokiej gamie przeglądarek, sieci i urządzeń można znacznie lepiej zrozumieć, jak technologia ta sprawdza się w prawdziwych sytuacjach, i odkryć cechy wydajności, których być może nie spodziewaliśmy się wcześniej.

Oto kilka kluczowych wniosków z badania IOWA:

  • Średnio strony wczytywały się nieco szybciej, gdy mechanizm Service Worker zarządzał stroną, niż miał to miejsce bez tego skryptu, zarówno w przypadku nowych, jak i powracających użytkowników.
  • Wizyty na stronach kontrolowane przez skrypt service worker ładowały się niemal natychmiast u wielu użytkowników.
  • Skrypty service worker, jeśli były nieaktywne, potrzebowały czasu na uruchomienie. Jednak nieaktywny skrypt service worker nadal działał lepiej niż żaden skrypt service worker.
  • Czas uruchamiania nieaktywnego skryptu service worker był dłuższy na urządzeniu mobilnym niż na komputerze.

Wzrost wydajności zaobserwowany w konkretnej aplikacji jest zwykle przydatny w raportach szerszej społeczności programistów, ale pamiętaj, że te wyniki zależą od typu witryny IOWA (witryna wydarzenia) oraz grupy odbiorców IOWA (głównie deweloperów).

Jeśli wdrażasz w swojej aplikacji mechanizm Service Worker, warto wdrożyć własną strategię pomiarową, aby móc ocenić własną skuteczność i zapobiec przyszłym regresjom. Jeśli tak, podziel się wynikami, żeby wszyscy mogli z nich skorzystać.

Przypisy

  1. Porównanie wydajności implementacji pamięci podręcznej naszych mechanizmów Service Worker z wydajnością naszej witryny z samą pamięcią podręczną HTTP jest niesprawiedliwe. Optymalizowaliśmy IOWA pod kątem mechanizmów Service Worker, dlatego nie poświęcaliśmy dużo czasu na optymalizację pod kątem pamięci podręcznej HTTP. Gdyby tak było, wyniki byłyby prawdopodobnie inne. Aby dowiedzieć się więcej o optymalizowaniu witryny pod kątem pamięci podręcznej HTTP, przeczytaj artykuł Wydajna optymalizacja treści.
  2. W zależności od sposobu, w jaki witryna wczytuje style i zawartość, może się zdarzyć, że przeglądarka będzie w stanie wyświetlić treść lub style, zanim staną się dostępne. W takich przypadkach firstpaint może odpowiadać pustemu, białym ekranie. Jeśli korzystasz z tagu firstpaint, musisz zadbać o to, aby odpowiadał on istotnemu punktowi wczytywania zasobów witryny.
  3. Technicznie rzecz biorąc, aby zarejestrować te informacje, a nie zdarzenia, moglibyśmy wysłać działanie czasu (domyślnie niewymagające interakcji). W rzeczywistości takie działania zostały dodane do Google Analytics w celu śledzenia danych dotyczących obciążenia. Jednak działania związane z czasem są w trakcie przetwarzania intensywnie próbkowane i ich wartości nie można używać w segmentach. Ze względu na te ograniczenia lepiej nadają się do tego zdarzenia niewymagające interakcji.
  4. Więcej informacji o zakresie niestandardowego wymiaru w Google Analytics znajdziesz w sekcji 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 (działań). Więcej dowiesz się z lekcji w Akademii Analytics poświęconej modelowi danych Google Analytics.
  5. Nie uwzględnia to zasobów leniwie ładowanych po zdarzeniu wczytywania.