Błyskawiczne wczytywanie aplikacji internetowych za pomocą architektury powłoki aplikacji

Addy Osmani
Addy Osmani

Powłoka systemowa aplikacji to krótki kod HTML, CSS i JavaScript, który tworzy interfejs użytkownika. Powłoka ta powinna:

  • szybkie ładowanie
  • być buforowana
  • dynamiczne wyświetlanie treści

Powłoka aplikacji to klucz do niezawodnie wysokiej wydajności. Tworząc aplikację natywną, możesz myśleć o powłoce aplikacji jak o pakiecie kodu, który należałoby opublikować w sklepie. Dzięki temu interfejs użytkownika jest lokalny, a treści są pobierane dynamicznie przez interfejs API.

Oddzielenie powłoki HTML, JS i CSS od zawartości HTML

Wprowadzenie

Artykuł Alex Russella na temat progresywnych aplikacji internetowych opisuje, jak aplikacja internetowa może stopniowo zmieniać się przez użytkowanie i wyrażanie zgody użytkownika, co zapewnia jej bardziej natywną obsługę, w tym obsługę offline, powiadomienia push i możliwość dodania jej do ekranu głównego. Zależy to w znacznej mierze od korzyści związanych z funkcjami i wydajnością skryptów service worker, a także od ich możliwości buforowania. W ten sposób możesz skupić się na szybkości, zapewniając swoim aplikacjom internetowym błyskawiczne wczytywanie i regularne aktualizacje, które znasz z aplikacji natywnych.

Aby w pełni wykorzystać te możliwości, potrzebujemy nowego spojrzenia na witryny: architektury powłoki aplikacji.

Z tego artykułu dowiesz się, jak utworzyć strukturę aplikacji za pomocą architektury rozszerzonej powłoki systemowej aplikacji Service Worker. Przyjrzymy się renderowaniu zarówno po stronie klienta, jak i po stronie serwera, a potem udostępnimy kompleksowy przykład, który możesz wypróbować już dziś.

Aby podkreślić tę kwestię, poniższy przykład pokazuje pierwsze wczytanie aplikacji korzystającej z tej architektury. Zwróć uwagę na komunikat „Aplikacja jest gotowa do użycia w trybie offline” u dołu ekranu. Jeśli aktualizacja powłoki będzie później dostępna, możemy poinformować użytkownika, że musi odświeżyć ją w celu pobrania nowej wersji.

Obraz skryptu service worker uruchomionego w Narzędziach deweloperskich na potrzeby powłoki aplikacji

Co to są mechanizmy Service Worker?

Skrypt service worker działa w tle niezależnie od Twojej strony internetowej. Reaguje na zdarzenia, w tym na żądania sieciowe wysyłane ze stron, które obsługuje, oraz powiadomienia push z Twojego serwera. Skrypt service worker ma celowo krótki czas działania. Wybudza się po wystąpieniu zdarzenia i działa tylko tak długo, jak to konieczne do przetworzenia zdarzenia.

Skrypty service worker mają też ograniczony zestaw interfejsów API w porównaniu z JavaScriptem w normalnym kontekście przeglądania. Jest to standard w przypadku pracowników w internecie. Skrypt service worker nie ma dostępu do DOM, ale może korzystać z takich funkcji jak Cache API. Może też wysyłać żądania sieciowe za pomocą Fetch API. Interfejsy IndexedDB API i postMessage() są też dostępne do użycia w celu zapewnienia trwałości danych i przesyłania wiadomości między skryptem service worker a kontrolowanymi stronami. Zdarzenia push wysyłane z Twojego serwera mogą wywoływać interfejs powiadomienia API, aby zwiększyć zaangażowanie użytkowników.

Skrypt service worker może przechwytywać żądania sieciowe wysyłane ze strony (wywołując w nim zdarzenie pobierania w skrypcie service worker) i zwracać odpowiedź pobraną z sieci lub z lokalnej pamięci podręcznej, a nawet skonstruowaną automatycznie. W rzeczywistości jest to programowalny serwer proxy w przeglądarce. Co ważne, niezależnie od tego, skąd pochodzi odpowiedź, strona internetowa wygląda tak, jakby nie korzystał z niego żaden pracownik service worker.

Aby dowiedzieć się więcej o mechanizmach Service Worker, przeczytaj wprowadzenie do mechanizmów Service Worker (w języku angielskim).

Korzyści z wydajności

Mechanizmy Service Worker sprawdzają się w pamięci podręcznej w trybie offline, ale zapewniają też znaczną poprawę wydajności w postaci błyskawicznego wczytywania w przypadku wielokrotnych wizyt w Twojej witrynie lub aplikacji internetowej. Możesz buforować powłokę aplikacji, aby działała offline, i wypełniać jej zawartość za pomocą JavaScriptu.

Przy ponownych wizytach pozwoli Ci to wyświetlić na ekranie istotne piksele bez połączenia z siecią, nawet jeśli ostatecznie z nich pochodzą treści. Pozwala natychmiast wyświetlać paski narzędzi i kart, a pozostałe treści wczytują stopniowo.

Aby przetestować tę architekturę na rzeczywistych urządzeniach, uruchomiliśmy przykład powłoki aplikacji na stronie WebPageTest.org i pokazaliśmy wyniki poniżej.

Test 1: testowanie na kablu z Nexusem 5 w wersji deweloperskiej Chrome

Pierwszy widok aplikacji musi pobrać wszystkie zasoby z sieci i nie zostanie wyrenderowany aż do 1,2 sekundy. Dzięki buforowaniu skryptu service worker podczas kolejnych wizyt następuje wyrenderowanie treści i całkowite wczytywanie się w ciągu 0, 5 sekundy.

Schemat malowania strony internetowej do testowania połączenia kablowego

Test 2: Testowanie w 3G na Nexusie 5 w Chrome Dev

Możemy też przetestować naszą próbkę przy nieco wolniejszym połączeniu 3G. Tym razem przy pierwszej wizycie potrzeba 2,5 sekundy, aby wykonać pierwsze znaczące wyrenderowanie. Pełne wczytanie strony zajmuje 7,1 sekundy. Dzięki buforowaniu skryptu service worker podczas kolejnych wizyt następuje wyrenderowanie treści i całkowite wczytywanie się w ciągu 0, 8 sekundy.

Schemat malowania strony internetowej dla połączenia 3G

Inne punkty widzenia przedstawiają podobną historię. Porównaj 3 sekundy potrzebne do uzyskania pierwszego znaczącego wyrenderowania w powłoce aplikacji:

Wyrenderuj oś czasu pierwszego widoku z testu stron internetowych

do 0,9 sekundy potrzebnej podczas wczytywania tej samej strony z naszej pamięci podręcznej skryptu service worker. Dzięki temu oszczędzamy użytkownikom czas dłuższy niż 2 sekundy.

Rysuj oś czasu dla widoku powtórnego z testu stron internetowych

Dzięki architekturze powłoki aplikacji Twoje własne aplikacje mogą osiągnąć podobny i niezawodny wzrost wydajności.

Czy mechanizm Service Worker wymaga od nas ponownej analizy struktury aplikacji?

Mechanizmy Service Worker sugerują drobne zmiany w architekturze aplikacji. Zamiast upychać całą aplikację w ciąg znaków HTML, warto wprowadzić zmiany w stylu AJAX. W tym przypadku jest dostępna powłoka (zawsze przechowywana w pamięci podręcznej i zawsze może się uruchomić bez połączenia z siecią) oraz treść, która jest regularnie odświeżana i zarządzana oddzielnie.

Konsekwencje tego podziału są znaczące. Przy pierwszej wizycie można wyrenderować zawartość na serwerze i zainstalować skrypt service worker po kliencie. Przy kolejnych wizytach potrzebujesz już tylko żądania danych.

A co z progresywnym ulepszaniem?

Skrypt service worker nie jest obecnie obsługiwany przez wszystkie przeglądarki, ale architektura powłoki treści aplikacji korzysta z progresywnego udoskonalania, aby zapewnić wszystkim dostęp do treści. Przeanalizujmy nasz przykładowy projekt.

Poniżej widać pełną wersję wyrenderowaną w Chrome, Firefox Nightly i Safari. Po lewej stronie widać wersję Safari, w której treść jest renderowana na serwerze bez skryptu service worker. Po prawej stronie widać wersje Chrome i Firefox Nightly oparte na mechanizmach Service Worker.

Obraz powłoki aplikacji wczytanej w Safari, Chrome i Firefoksie

Kiedy warto skorzystać z tej architektury?

Architektura powłoki systemowej aplikacji najlepiej sprawdza się w przypadku dynamicznych aplikacji i witryn. Jeśli Twoja witryna jest mała i statyczna, prawdopodobnie nie potrzebujesz powłoki aplikacji. Możesz po prostu zapisać całą witrynę w pamięci podręcznej, wykonując krok oninstall skryptu service worker. Użyj podejścia, które najlepiej pasuje do Twojego projektu. Wiele platform JavaScript już zachęca do podziału logiki aplikacji od treści, dzięki czemu można łatwiej zastosować ten wzorzec.

Czy są już jakieś aplikacje produkcyjne używające tego wzorca?

Architektura powłoki aplikacji jest możliwa po wprowadzeniu kilku zmian w ogólnym interfejsie aplikacji. Sprawdza się ona dobrze w przypadku dużych witryn, takich jak progresywna aplikacja internetowa Google I/O 2015 czy skrzynka odbiorcza Google.

Obraz ładowania skrzynki odbiorczej Google. Pokazuje skrzynkę odbiorczą za pomocą skryptu service worker.

Powłoki aplikacji offline to duży sukces w zakresie wydajności, które dobrze widać też w aplikacji Wikipedia offline Jake Archibalda i w progresywnej aplikacji internetowej Flipkart Lite.

Zrzuty ekranu z prezentacją Jake Archibalda w Wikipedii.

Omawianie architektury

Przy pierwszym wczytywaniu Twoim celem jest jak najszybsze wyświetlenie istotnych treści na ekranie użytkownika.

Pierwsze wczytanie i wczytanie innych stron

Schemat pierwszego wczytywania za pomocą powłoki aplikacji

Ogólnie architektura powłoki aplikacji:

  • Nadaj priorytet początkowym wczytywaniu, ale pozwól skryptowi service worker zapisywać powłokę aplikacji w pamięci podręcznej, tak aby wielokrotne wizyty nie wymagały ponownego pobierania powłoki z sieci.

  • Leniwe ładowanie lub ładowanie w tle. Jedną z możliwości jest użycie buforowania w trakcie odczytu w przypadku zawartości dynamicznej.

  • Użyj narzędzi service worker, takich jak sw-precache, aby niezawodnie buforować i aktualizować skrypt service worker zarządzający treścią statyczną. (Więcej o sw-precache później).

Aby to zrobić:

  • Serwer wyśle zawartość HTML, którą klient może wyrenderować, i użyje odległych nagłówków wygaśnięcia pamięci podręcznej HTTP, aby uwzględnić przeglądarki bez obsługi mechanizmów Service Worker. Będzie udostępniać nazwy plików z wykorzystaniem haszów, aby umożliwić obsługę wersji oraz łatwe aktualizacje na późniejszych etapach cyklu życia aplikacji.

  • Strony będą zawierały style CSS wbudowane w tagu <style> w dokumencie <head>, aby umożliwić szybkie wyrenderowanie powłoki aplikacji. Każda strona będzie asynchronicznie wczytywać kod JavaScript niezbędny dla bieżącego widoku. Arkusza CSS nie można ładować asynchronicznie, więc możemy żądać stylów przy użyciu JavaScriptu, ponieważ jest to działanie asynchroniczne, a nie synchroniczne i oparte na parserze. Możemy też korzystać z requestAnimationFrame(), aby uniknąć szybkiego ataku w pamięci podręcznej, przez co style przypadkowo stały się częścią krytycznej ścieżki renderowania. requestAnimationFrame() wymusza wyrenderowanie pierwszej klatki przed załadowaniem stylów. Innym sposobem jest użycie projektów takich jak loadCSS firmy Filament Group do asynchronicznego żądania CSS za pomocą JavaScriptu.

  • Skrypt service worker przechowuje w pamięci podręcznej wpis powłoki aplikacji, dzięki czemu przy kolejnych wizytach powłoka może być w całości ładowana w całości z tej pamięci, chyba że w sieci jest dostępna aktualizacja.

Powłoka aplikacji na treści

Praktyczne wdrożenie

Opracowaliśmy w pełni działający przykład wykorzystujących architekturę powłoki aplikacji: kod JavaScript vanilla ES2015 dla klienta i Express.js dla serwera. Nic nie stoi na przeszkodzie używania własnego stosu dla części klienta lub serwera (np.PHP, Ruby, Python).

Cykl życia skryptu service worker

W projekcie powłoki aplikacji używamy kodu sw-precache, który ma taki cykl życia:

Zdarzenie Działanie
Zainstaluj Buforuj powłokę aplikacji i inne zasoby aplikacji na jednej stronie.
Aktywuj Wyczyść stare pamięć podręczną.
Pobierz Wyświetlaj aplikację internetową z jedną stroną dla adresów URL oraz korzystaj z pamięci podręcznej w przypadku zasobów i zdefiniowanych wstępnie części. Użyj sieci do innych żądań.

Bity serwera

W tej architekturze komponent po stronie serwera (w naszym przypadku w wersji Express) powinien być w stanie oddzielnie traktować treść i prezentację. Można ją dodać do układu HTML, który powoduje statyczne wyrenderowanie strony, lub wyświetlać ją osobno i wczytywać dynamicznie.

Co oczywiste, konfiguracja po stronie serwera może znacznie się różnić od konfiguracji używanej w przypadku naszej aplikacji demonstracyjnej. Ten wzorzec aplikacji internetowych jest możliwy do zrealizowania w większości konfiguracji serwerów, choć jego wymaga pewnych zmian w architekturze. Ustaliliśmy, że następujący model działa dość dobrze:

Schemat architektury App Shell
  • Punkty końcowe są zdefiniowane dla 3 części aplikacji: adresu URL widocznego dla użytkownika (indeks/symbol wieloznaczny), powłoka aplikacji (skrypt service worker) i fragmenty kodu HTML.

  • Każdy punkt końcowy ma kontroler, który pobiera układ ręki, co z kolei może pobierać części i widoki kierownicy. Mówiąc najprościej, wyświetlenia to fragmenty kodu HTML, które są kopiowane na ostatnią stronę. Uwaga: platformy JavaScript, które wykonują bardziej zaawansowaną synchronizację danych, są często o wiele łatwiejsze do przeniesienia do architektury powłoki aplikacji. Zwykle korzystają one z wiązań danych i synchronizacji, a nie tylko do częściowych danych.

  • Użytkownik początkowo widzi statyczną stronę z treścią. Ta strona rejestruje skrypt service worker, jeśli jest obsługiwany, który zapisuje w pamięci podręcznej powłokę aplikacji i wszystko, od czego zależy (CSS, JS itp.).

  • Powłoka internetowa działa wtedy jak aplikacja internetowa na jednej stronie, wykorzystując kod JavaScript do XHR w treści dla określonego adresu URL. Wywołania XHR są wysyłane do punktu końcowego /partials*, który zwraca niewielki fragment kodu HTML, CSS i JS potrzebnego do wyświetlenia treści. Uwaga: można to osiągnąć na wiele sposobów, a XHR to tylko jeden z nich. Niektóre aplikacje umieszczają swoje dane w tekście (być może korzystają z formatu JSON) na potrzeby początkowego renderowania, więc nie są „statyczne” w kontekście spłaszczonego kodu HTML.

  • Przeglądarki bez obsługi mechanizmów Service Worker powinny zawsze korzystać z ustawień zastępczych. W naszej demonstracji korzystamy z podstawowego renderowania statycznego po stronie serwera, ale to tylko jedna z wielu opcji. Aspekt działania mechanizmu Service Worker daje nowe możliwości zwiększania wydajności jednostronicowej aplikacji w stylu aplikacji za pomocą powłoki aplikacji przechowywanej w pamięci podręcznej.

Obsługa wersji plików

Pojawia się pytanie, jak obsługiwać obsługę wersji i aktualizowanie plików. Jest to zależne od aplikacji, a dostępne opcje to:

  • Sieć musi być najpierw dostępna, a w przeciwnym razie użyj wersji z pamięci podręcznej.

  • Tylko sieć i niepowodzenie w przypadku połączenia offline.

  • Zapisz w pamięci podręcznej starą wersję i zaktualizuj później.

W przypadku powłoki aplikacji najważniejsza jest pamięć podręczna przy konfigurowaniu skryptu service worker. Jeśli nie buforujesz powłoki aplikacji, nie została prawidłowo przyjęta architektura.

Narzędzia

Udostępniamy wiele różnych bibliotek pomocniczych skryptu service worker, które ułatwiają konfigurację wstępnego buforowania powłoki aplikacji lub obsługi typowych wzorców buforowania.

Zrzut ekranu przedstawiający witrynę biblioteki mechanizmów Service Worker w Web Fundamentals

Używanie pliku sw-precache na potrzeby powłoki aplikacji

Użycie metody sw-precache do buforowania powłoki aplikacji powinno rozwiązać problemy z wersjami plików, pytaniami dotyczącymi instalacji i aktywacji oraz scenariuszem pobierania dotyczącym powłoki aplikacji. Wklej sw-precache do procesu kompilacji aplikacji i użyj konfigurowalnych symboli wieloznacznych, aby pobrać zasoby statyczne. Zamiast ręcznie tworzyć skrypt skryptu service worker, zezwól skryptowi sw-precache na generowanie takiego, który będzie zarządzał pamięcią podręczną w bezpieczny i wydajny sposób, przy użyciu modułu obsługi pobierania na pierwszym miejscu.

Wstępne wizyty w aplikacji aktywują zapisywanie w pamięci podręcznej wszystkich potrzebnych zasobów. Przypomina to instalowanie aplikacji natywnych ze sklepu z aplikacjami. Gdy użytkownicy wracają do aplikacji, pobierane są tylko zaktualizowane zasoby. W wersji demonstracyjnej informujemy użytkowników o udostępnieniu nowej powłoki komunikatem „Aktualizacje aplikacji. Odśwież, aby pobrać nową wersję”. Pozwala on w prosty sposób poinformować użytkowników, że mogą odświeżyć stronę, aby pobrać najnowszą wersję.

Używanie narzędzia sw-toolbox do buforowania środowiska wykonawczego

Użyj narzędzia sw-toolbox do buforowania środowiska wykonawczego z różnymi strategiami w zależności od zasobu:

  • cacheFirst na potrzeby obrazów oraz dedykowana nazwana pamięć podręczna z niestandardową zasadą wygaśnięcia równą N maxEntries.

  • networkFirst lub najszybciej w przypadku żądań do interfejsu API, w zależności od żądanej aktualności treści. Najszybszym rozwiązaniem może być rozwiązanie, ale jeśli istnieje konkretny plik danych interfejsu API, który jest często aktualizowany, używaj wartości networkFirst.

Podsumowanie

Architektura powłoki aplikacji ma kilka zalet, ale ma sens tylko w przypadku niektórych klas aplikacji. Model jest jeszcze młody i warto ocenić nakład pracy i ogólne korzyści płynące z jego zastosowania.

W naszych eksperymentach korzystaliśmy z udostępniania szablonów między klientem a serwerem, aby zminimalizować nakład pracy związany z tworzeniem dwóch warstw aplikacji. Dzięki temu stopniowe ulepszanie tej funkcji jest nadal podstawową funkcją.

Jeśli zastanawiasz się nad użyciem mechanizmów Service Worker w swojej aplikacji, przyjrzyj się architekturze i oceń, czy sprawdzi się ona w Twoich projektach.

Dzięki weryfikatorom: Jeff Posnick, Paul Lewis, Alex Russell, Seth Thompson, Rob Dodson, Taylor Savage i Joe Medley.