Jak budować pierwsze progresywne aplikacje internetowe (PWAs) w trybie offline z React & Redux?

Na całym świecie jest jeszcze wiele miejsc, gdzie dostęp do Internetu jest albo słaby, albo nie ma go wcale. Najlepszym przykładem jest samolot lub piwnica fabryczna - ale oczywiście to tylko wierzchołek góry lodowej. Jeśli oferujesz aplikację mobilną, to wyzwaniem jest, aby działała ona w takich warunkach.

Miałem takie wyzwanie w moim ostatnim projekcie i postanowiłem podzielić się swoim doświadczeniem. Teraz nadszedł czas, aby dać Ci kilka wskazówek, jak budować aplikacje internetowe, które działają w pełni offline.

Jak to działa domyślnie

PWAs to aplikacje internetowe z dodatkowymi możliwościami. Domyślnie, tryb offline powita Cię niesławnym T-Rexem (przynajmniej jeśli używasz Chrome) tak jak na każdej innej stronie internetowej - ale mamy wszystkie narzędzia, aby to zmienić!

PWA w trybie offline bez buforowania

Czytaj Odporność

Celem jest zaoferowanie użytkownikom dostępu do plików aplikacji i danych, które wcześniej widzieli bez dostępu do sieci. Jest to idealne rozwiązanie dla serwisu Worker - prefetching i buforowania danych do późniejszego wykorzystania. To nic wielkiego mówić SW, aby buforował wszystkie zasoby, które uważają Państwo za niezbędne do uruchomienia swojego PWA. Jeśli uruchomisz swój projekt za pomocą CRA , masz już zdefiniowany zestaw reguł i dołączony skrypt do generowania pracowników serwisu.

Ale jest jeden problem - nie są one zbyt elastyczne. W związku z tym użyłem własnej konfiguracji z Workboksem. Więcej szczegółów na temat tego, jak korzystać z własnego pracownika serwisu w CRA, można znaleźć tutaj. Domyślna konfiguracja jest wystarczająco dobra, jeśli zależy ci tylko na cache'u statycznych aktywów, takich jak JS, pliki CSS, czcionki czy obrazki. Potrzebowałem czegoś more - translations oraz danych, które w moim przypadku pochodziły z naszego REST API.

Ponieważ /translations/{locale} punkt końcowy był czymś, czego nie trzymałem w stanie aplikacji (zarządzanej przez Redux), ale po prostu przekazałem go do wtyczki i18next, pomyślałem, że to dobry pomysł, aby powiedzieć Pracownikowi Serwisu, aby buforować jego odpowiedź, jak również.

Poniżej możesz zobaczyć moją konfigurację z cache'owaniem tłumaczeń w zestawie:

Ale co z danymi pochodzącymi z API? Wspomniałem, że stan aplikacji jest zarządzany przez Redux. Nie zagłębię się zbytnio w to, czym jest Redux - jeśli nie znasz go zbyt dobrze, prawdopodobnie przynajmniej o nim słyszałeś. Ale jedną ważną rzeczą, którą musisz pamiętać o Reduksie jest to, że stan aplikacji jest przechowywany w jednym obiekcie zwanym sklepem Reduxa. A ponieważ jest to pojedynczy obiekt, łatwo jest również zapisać ten obiekt gdzieś w przeglądarce.

Istnieje pakiet, który robi dokładnie to, co nazywa się redux-persist. Domyślnie zapisuje on stan w localStorage, dzięki czemu możesz nadal wyświetlać wszystko, co wcześniej miałeś w swoim sklepie Redux w trybie offline. Można również użyć innego mechanizmu pamięci masowej, np. IndexedDB. Może to być lepszy wybór, jeśli martwisz się o rozmiar pamięci masowej.

Uwaga boczna: Redux-persist jest bardzo popularnym pakietem w rozwoju React Native, który naśladuje zachowanie Native App w zakresie utrzymywania stanu pomiędzy sesjami w AsyncStorage.

Po wykonaniu tych wszystkich kroków, użytkownik może teraz uzyskać dostęp do aplikacji w trybie offline. Gratulacje!

Ale to był tylko pierwszy krok.

PWA w trybie offline z buforowaniem SW i redux-persistem

Napisać Odporność

Jak widać na ekranach, nasza aplikacja posiada kilka ikon "plus". Możemy zatrzymać się tutaj i powiedzieć "Przepraszam, dodawanie lub edytowanie czegokolwiek nie jest obsługiwane w trybie offline", co jest idealne dla niektórych firm. Ale co jeśli ta funkcja jest bardzo pożądana (lub nawet kluczowa) dla danego biznesu?
Po pierwsze, skupmy się na tym, jak zwykle rozmawiamy z API i reagujemy na jego reakcję:

Dodanie nowego kontaktu  -  UI jest aktualizowany po udanej odpowiedzi API.

To jest w porządku dla aplikacji, które wymagają połączenia z Internetem, aby być dostępne. Ponieważ oczywiście nie możemy polegać na API w offline mode ,  maybe powinniśmy od razu zaktualizować UI i uruchomić aktualizację API w tle?

Optymistyczny interfejs użytkownika

Istnieje wzorzec UX dla takiego zachowania zwany optymistycznym UI. Wydaje się to być dobrym wyborem w naszym przypadku.
W tym podejściu aktualizujemy UI niezależnie od odpowiedzi API, zakładając, że dla większości żądań zakończy się to sukcesem. Oto jak będzie wyglądać dodanie nowego kontaktu z tym wzorcem:

Dodanie nowego kontaktu  -  Optimistic Podejście UI

Jaka będzie różnica w trybie offline? Jeśli chodzi o UI, to nie much  -  we po prostu nie pokaże liniowego postępu, ponieważ nie możemy nazwać API w trybie offline. Mimo to, musimy się upewnić, że to połączenie zostanie wywołane po powrocie do trybu offline. Moglibyśmy sami zaimplementować tę logikę, ale zamiast na nowo wymyślać koło, zobaczmy co ma do zaoferowania pakiet redux offline.

Wygląda na to, że ma wszystko, czego potrzebujemy... a nawet więcej! Aby wymienić tylko kilka kluczowych funkcji:

  • Wbudowany redux-persist, aby zapisać stan dla offline z możliwością wyłączenia i korzystania z niestandardowych ustawień
  • Mechanizm wykrywania w trybie offline/Online
  • Zarządzanie kolejką wniosków
  • Mechanizm retencji w przypadku sieci płatków

Jedyną rzeczą, o którą musimy się upewnić po utworzeniu biblioteki, jest użycie zawsze określonego formatu akcji redukcyjnych dla wszystkich, którzy muszą wysyłać dane na backend. Oto przykład, jak wygląda dodawanie nowego kontaktu z redux offline:
W tym przypadku optymistyczna aktualizacja zostanie wykonana zaraz po wysłaniu osoby kontaktowej/ akcji redukcyjnej. Pozostałe 2 akcje zostaną wysłane tylko wtedy, gdy użytkownik jest online.

Logika wysyłania akcji Redux-offline: Logika wysyłania akcji Redux-offline

Jedynym kawałkiem układanki, którego jeszcze nie wyjaśniłem, jest to, dlaczego po stronie klienta generujemy identyfikatory dla nowych podmiotów. To z powodu relacji.
Wyobraźmy sobie, że chcemy wykorzystać nasz nowo utworzony kontakt w trybie offline w innym żądaniu. Można by na przykład przypisać kontakt do notatki z wizyty. Pamiętajmy, że nadal jesteśmy w trybie offline. Aby nie powielać danych, ładunek w tym nowym zgłoszeniu powinien zawierać ID kontaktu, który wcześniej utworzyliśmy.

1
2
3
4
5
6
PUT /wizwizja-not/1
{
wizytaData: "2020-10-23",
uwaga: "Moja wizyta",
contactPersonId: "uuid-generated-on-client-side
}

Oczywiście, ponieważ poprzednie żądanie POST nie trafiło jeszcze do API, nie możemy polegać na ID generowanych po stronie serwera.
Gdy tylko połączenie zostanie przywrócone, wszystkie żądania z kolejki są wyzwalane jeden po drugim w kolejności, w jakiej zostały umieszczone w kolejce offline.
Mimo to, mamy teraz możliwość modyfikacji danych w trybie offline.

Wniosek

Nie jest trywialnym zadaniem dodawanie wsparcia offline do swojej aplikacji. Na szczęście, dzięki pracownikom obsługi i bibliotekom takim jak redux-persist i redux offline, nie jest to ogromny wysiłek związany z rozwojem - wystarczy zrozumieć koncepcje, aby używać ich bez zamieszania.

Miejmy nadzieję, że ten artykuł ujawni trochę magii kryjącej się za offline'ową architekturą i pomoże Ci włączyć tę funkcjonalność do Twoich projektów!