Te ćwiczenia z programowania są częścią kursu szkoleniowego „Tworzenie progresywnych aplikacji internetowych”, który został opracowany przez zespół Google Developers Training. Najwięcej korzyści przyniesie Ci ukończenie wszystkich ćwiczeń w kolejności.
Szczegółowe informacje o szkoleniu znajdziesz w omówieniu tworzenia progresywnych aplikacji internetowych.
Wprowadzenie
W tym module dowiesz się, jak używać Fetch API, prostego interfejsu do pobierania zasobów, który jest ulepszoną wersją interfejsu XMLHttpRequest API.
Czego się nauczysz
- Jak używać interfejsu Fetch API do wysyłania żądań dotyczących zasobów
- Jak wysyłać żądania GET, HEAD i POST za pomocą funkcji fetch
- Jak odczytywać i ustawiać niestandardowe nagłówki
- Korzystanie z CORS i jego ograniczenia
Co warto wiedzieć
- Podstawowy JavaScript i HTML
- Znajomość koncepcji i podstawowej składni obietnic ES2015.
Wymagania
- Komputer z dostępem do terminala lub powłoki
- Połączenie z internetem
- Przeglądarka obsługująca Fetch
- edytor tekstu,
- Node i npm
Uwaga: interfejs Fetch API nie jest obecnie obsługiwany we wszystkich przeglądarkach, ale istnieje polyfill.
Pobierz lub sklonuj repozytorium pwa-training-labs z GitHub i w razie potrzeby zainstaluj wersję LTS Node.js.
Otwórz wiersz polecenia na komputerze. Przejdź do katalogu fetch-api-lab/app/
i uruchom lokalny serwer programistyczny:
cd fetch-api-lab/app npm install node server.js
W dowolnej chwili możesz zamknąć serwer, klikając Ctrl-c
.
Otwórz przeglądarkę i wejdź na stronę localhost:8081/
. Powinna wyświetlić się strona z przyciskami do wysyłania próśb (jeszcze nie będą działać).
Uwaga: wyrejestruj wszystkie skrypty service worker i wyczyść wszystkie ich pamięci podręczne na potrzeby hosta lokalnego, aby nie zakłócały działania modułu. W Narzędziach deweloperskich w Chrome możesz to zrobić, klikając Wyczyść dane witryny w sekcji Wyczyść pamięć na karcie Aplikacja.
Otwórz folder fetch-api-lab/app/
w preferowanym edytorze tekstu. W folderze app/
będziesz tworzyć laboratorium.
Ten folder zawiera:
echo-servers/
zawiera pliki używane do uruchamiania serwerów testowych.examples/
zawiera przykładowe zasoby, których używamy do eksperymentowania z pobieraniem.js/main.js
to główny plik JavaScript aplikacji, w którym będziesz pisać cały kod.index.html
to główna strona HTML naszej przykładowej witryny lub aplikacji.package-lock.json
ipackage.json
to pliki konfiguracyjne naszego serwera deweloperskiego i zależności serwera echo.server.js
to serwer programisty węzła
Interfejs Fetch API jest stosunkowo prosty. Z tej sekcji dowiesz się, jak napisać podstawowe żądanie HTTP za pomocą funkcji fetch.
Pobieranie pliku JSON
W js/main.js
przycisk Fetch JSON (Pobierz JSON) aplikacji jest połączony z funkcją fetchJSON
.
Zaktualizuj funkcję fetchJSON
, aby wysyłała żądanie pliku examples/animals.json
i rejestrowała odpowiedź:
function fetchJSON() {
fetch('examples/animals.json')
.then(logResult)
.catch(logError);
}
Zapisz skrypt i odśwież stronę. Kliknij Pobierz JSON. Konsola powinna rejestrować odpowiedź pobierania.
Wyjaśnienie
Metoda fetch
przyjmuje ścieżkę do zasobu, który chcemy pobrać, jako parametr. W tym przypadku jest to examples/animals.json
. fetch
zwraca obietnicę, która jest rozwiązywana jako obiekt Response. Jeśli obietnica zostanie spełniona, odpowiedź zostanie przekazana do funkcji logResult
. Jeśli obietnica zostanie odrzucona, kontrolę przejmuje catch
, a błąd jest przekazywany do funkcji logError
.
Obiekty odpowiedzi reprezentują odpowiedź na żądanie. Zawierają one treść odpowiedzi, a także przydatne właściwości i metody.
Testowanie nieprawidłowych odpowiedzi
Sprawdź zalogowaną odpowiedź w konsoli. Zanotuj wartości właściwości status
, url
i ok
.
Zastąp zasób examples/animals.json
w fetchJSON
zasobem examples/non-existent.json
. Zaktualizowana funkcja fetchJSON
powinna teraz wyglądać tak:
function fetchJSON() {
fetch('examples/non-existent.json')
.then(logResult)
.catch(logError);
}
Zapisz skrypt i odśwież stronę. Kliknij ponownie Pobierz JSON, aby spróbować pobrać ten nieistniejący zasób.
Zwróć uwagę, że pobieranie zostało zakończone i nie spowodowało zablokowania catch
. Teraz znajdź właściwości status
, URL
i ok
nowej odpowiedzi.
Wartości w tych 2 plikach powinny się różnić (czy wiesz dlaczego?). Jeśli wystąpiły jakieś błędy w konsoli, czy wartości są zgodne z kontekstem błędu?
Wyjaśnienie
Dlaczego nieudana odpowiedź nie aktywowała bloku catch
? Ważna uwaga dotycząca pobierania i obietnic: nieprawidłowe odpowiedzi (np. 404) nadal są rozwiązywane. Obietnica pobierania jest odrzucana tylko wtedy, gdy nie można ukończyć żądania, więc zawsze musisz sprawdzać poprawność odpowiedzi. Odpowiedzi zweryfikujemy w następnej sekcji.
Więcej informacji
Sprawdzanie ważności odpowiedzi
Musimy zaktualizować nasz kod, aby sprawdzać ważność odpowiedzi.
W pliku main.js
dodaj funkcję sprawdzania poprawności odpowiedzi:
function validateResponse(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
Następnie zastąp zmienną fetchJSON
tym kodem:
function fetchJSON() {
fetch('examples/non-existent.json')
.then(validateResponse)
.then(logResult)
.catch(logError);
}
Zapisz skrypt i odśwież stronę. Kliknij Pobierz JSON. Sprawdź konsolę. Odpowiedź na examples/non-existent.json
powinna teraz wywołać blok catch
.
Zastąp examples/non-existent.json
w funkcji fetchJSON
oryginalnym examples/animals.json
. Zaktualizowana funkcja powinna teraz wyglądać tak:
function fetchJSON() {
fetch('examples/animals.json')
.then(validateResponse)
.then(logResult)
.catch(logError);
}
Zapisz skrypt i odśwież stronę. Kliknij Pobierz JSON. Powinna pojawić się informacja o tym, że odpowiedź została zarejestrowana.
Wyjaśnienie
Po dodaniu sprawdzania validateResponse
nieprawidłowe odpowiedzi (np. 404) powodują błąd i przejmuje je catch
. Dzięki temu możemy obsługiwać nieudane odpowiedzi i zapobiegać rozprzestrzenianiu się nieoczekiwanych odpowiedzi w łańcuchu pobierania.
Przeczytaj odpowiedź
Odpowiedzi Fetch są reprezentowane jako ReadableStreams (specyfikacja strumieni) i muszą zostać odczytane, aby uzyskać dostęp do treści odpowiedzi. Obiekty odpowiedzi mają do tego metody.
W pliku main.js
dodaj funkcję readResponseAsJSON
z tym kodem:
function readResponseAsJSON(response) {
return response.json();
}
Następnie zastąp funkcję fetchJSON
tym kodem:
function fetchJSON() {
fetch('examples/animals.json') // 1
.then(validateResponse) // 2
.then(readResponseAsJSON) // 3
.then(logResult) // 4
.catch(logError);
}
Zapisz skrypt i odśwież stronę. Kliknij Pobierz JSON. Sprawdź w konsoli, czy rejestrowany jest kod JSON z examples/animals.json
(zamiast obiektu Response).
Wyjaśnienie
Przyjrzyjmy się temu, co się dzieje.
Krok 1. Wywołanie pobierania w przypadku zasobu examples/animals.json
. Funkcja fetch zwraca obietnicę, która jest rozwiązywana jako obiekt Response. Gdy obietnica zostanie spełniona, obiekt odpowiedzi zostanie przekazany do funkcji validateResponse
.
Krok 2. validateResponse
sprawdza, czy odpowiedź jest prawidłowa (czy ma kod 200). Jeśli tak nie jest, wystąpi błąd, który spowoduje pominięcie pozostałych bloków then
i wywołanie bloku catch
. Jest to szczególnie ważne. Bez tego sprawdzania nieprawidłowe odpowiedzi są przekazywane dalej w łańcuchu i mogą powodować problemy w późniejszym kodzie, który może polegać na otrzymywaniu prawidłowej odpowiedzi. Jeśli odpowiedź jest prawidłowa, jest przekazywana do funkcji readResponseAsJSON
.
Krok 3. readResponseAsJSON
odczytuje treść odpowiedzi za pomocą metody Response.json(). Ta metoda zwraca obietnicę, która jest rozwiązywana w formacie JSON. Gdy obietnica zostanie spełniona, dane JSON zostaną przekazane do funkcji logResult
. (Jeśli obietnica z response.json()
zostanie odrzucona, uruchomiony zostanie blok catch
).
Krok 4. Na koniec logResult
rejestruje dane JSON z pierwotnego żądania wysłanego do examples/animals.json
.
Więcej informacji
Pobieranie nie jest ograniczone do formatu JSON. W tym przykładzie pobierzemy obraz i dodamy go do strony.
W pliku main.js
napisz funkcję showImage
z tym kodem:
function showImage(responseAsBlob) {
const container = document.getElementById('img-container');
const imgElem = document.createElement('img');
container.appendChild(imgElem);
const imgUrl = URL.createObjectURL(responseAsBlob);
imgElem.src = imgUrl;
}
Następnie dodaj funkcję readResponseAsBlob
, która odczytuje odpowiedzi jako Blob:
function readResponseAsBlob(response) {
return response.blob();
}
Zaktualizuj funkcję fetchImage
za pomocą tego kodu:
function fetchImage() {
fetch('examples/fetching.jpg')
.then(validateResponse)
.then(readResponseAsBlob)
.then(showImage)
.catch(logError);
}
Zapisz skrypt i odśwież stronę. Kliknij Pobierz obraz. Na stronie powinien się wyświetlić uroczy pies aportujący patyk (to żart związany z aportowaniem).
Wyjaśnienie
W tym przykładzie pobierany jest obraz examples/fetching.jpg
. Podobnie jak w poprzednim ćwiczeniu odpowiedź jest weryfikowana za pomocą validateResponse
. Odpowiedź jest następnie odczytywana jako Blob (zamiast JSON, jak w poprzedniej sekcji). Element obrazu jest tworzony i dołączany do strony, a atrybut src
obrazu jest ustawiany na adres URL danych reprezentujący obiekt Blob.
Uwaga: do wygenerowania adresu URL danych reprezentującego obiekt Blob używana jest metoda createObjectURL()
obiektu URL. Warto o tym pamiętać. Nie możesz ustawić źródła obrazu bezpośrednio na obiekt Blob. Obiekt Blob musi zostać przekonwertowany na adres URL danych.
Więcej informacji
Ta sekcja jest opcjonalnym wyzwaniem.
Zaktualizuj funkcję fetchText
, aby
- pobieranie
/examples/words.txt
- zweryfikować odpowiedź za pomocą
validateResponse
. - odczytać odpowiedź jako tekst (wskazówka: zobacz Response.text());
- i wyświetlić tekst na stronie.
Możesz użyć tej funkcji showText
jako pomocnika do wyświetlania tekstu końcowego:
function showText(responseAsText) {
const message = document.getElementById('message');
message.textContent = responseAsText;
}
Zapisz skrypt i odśwież stronę. Kliknij Pobierz tekst. Jeśli fetchText
został wdrożony prawidłowo, na stronie powinien być widoczny dodany tekst.
Uwaga: choć może się wydawać, że pobieranie kodu HTML i dołączanie go za pomocą atrybutu innerHTML
jest dobrym pomysłem, zachowaj ostrożność. Może to narazić Twoją witrynę na ataki typu cross-site scripting.
Więcej informacji
Domyślnie funkcja fetch używa metody GET, która pobiera określony zasób. Ale funkcja fetch może też używać innych metod HTTP.
Wysyłanie żądania HEAD
Zastąp funkcję headRequest
tym kodem:
function headRequest() {
fetch('examples/words.txt', {
method: 'HEAD'
})
.then(validateResponse)
.then(readResponseAsText)
.then(logResult)
.catch(logError);
}
Zapisz skrypt i odśwież stronę. Kliknij HEAD request (Żądanie HEAD). Zauważ, że zalogowana treść tekstowa jest pusta.
Wyjaśnienie
Metoda fetch
może przyjmować drugi parametr opcjonalny, init
. Ten parametr umożliwia konfigurację żądania pobierania, np. metody żądania, trybu pamięci podręcznej, danych logowania i innych.
W tym przykładzie ustawiamy metodę żądania pobierania na HEAD za pomocą parametru init
. Żądania HEAD są podobne do żądań GET, z tym że treść odpowiedzi jest pusta. Tego rodzaju żądanie można wykorzystać, gdy potrzebne są tylko metadane pliku, a nie wszystkie jego dane.
Opcjonalnie: sprawdzanie rozmiaru zasobu
Sprawdźmy nagłówki odpowiedzi pobierania dla examples/words.txt
, aby określić rozmiar pliku.
Zmodyfikuj funkcję headRequest
tak, aby rejestrowała właściwość content-length
odpowiedzi headers
(wskazówka: zapoznaj się z dokumentacją nagłówków i metodą get).
Po zaktualizowaniu kodu zapisz plik i odśwież stronę. Kliknij HEAD request (Żądanie HEAD). Konsola powinna rejestrować rozmiar (w bajtach) elementu examples/words.txt
.
Wyjaśnienie
W tym przykładzie metoda HEAD służy do żądania rozmiaru (w bajtach) zasobu (reprezentowanego w nagłówku content-length
) bez faktycznego wczytywania samego zasobu. W praktyce można tego użyć do określenia, czy należy wysłać żądanie pełnego zasobu (a nawet jak to zrobić).
Opcjonalnie: sprawdź rozmiar examples/words.txt
inną metodą i upewnij się, że jest on zgodny z wartością z nagłówka odpowiedzi (możesz sprawdzić, jak to zrobić w przypadku konkretnego systemu operacyjnego – dodatkowe punkty za użycie wiersza poleceń).
Więcej informacji
Pobieranie może też wysyłać dane za pomocą żądań POST.
Konfigurowanie serwera echo
W tym przykładzie musisz uruchomić serwer echo. W katalogu fetch-api-lab/app/
uruchom to polecenie (jeśli wiersz poleceń jest blokowany przez serwer localhost:8081
, otwórz nowe okno lub kartę wiersza poleceń):
node echo-servers/cors-server.js
To polecenie uruchamia prosty serwer pod adresem localhost:5000/
, który powtarza wysłane do niego żądania.
W każdej chwili możesz zamknąć ten serwer, klikając ctrl+c
.
Wysyłanie żądania POST
Zastąp funkcję postRequest
tym kodem (jeśli nie została jeszcze zdefiniowana funkcja showText
z sekcji 4, zrób to teraz):
function postRequest() {
fetch('http://localhost:5000/', {
method: 'POST',
body: 'name=david&message=hello'
})
.then(validateResponse)
.then(readResponseAsText)
.then(showText)
.catch(logError);
}
Zapisz skrypt i odśwież stronę. Kliknij POST request (ŻĄDANIE POST). Sprawdź, czy wysłana prośba została odzwierciedlona na stronie. Powinien zawierać imię i nazwisko oraz wiadomość (pamiętaj, że nie pobieramy jeszcze danych z formularza).
Wyjaśnienie
Aby wysłać żądanie POST za pomocą funkcji fetch, używamy parametru init
do określenia metody (podobnie jak w przypadku metody HEAD w poprzedniej sekcji). W tym miejscu ustawiamy też treść żądania, w tym przypadku prosty ciąg znaków. Treść to dane, które chcemy wysłać.
Uwaga: w środowisku produkcyjnym zawsze szyfruj wszystkie wrażliwe dane użytkownika.
Gdy dane są wysyłane jako żądanie POST do localhost:5000/
, żądanie jest zwracane jako odpowiedź. Odpowiedź jest następnie weryfikowana za pomocą validateResponse
, odczytywana jako tekst i wyświetlana na stronie.
W praktyce ten serwer reprezentuje interfejs API innej firmy.
Opcjonalnie: użyj interfejsu FormData
Za pomocą interfejsu FormData możesz łatwo pobierać dane z formularzy.
W funkcji postRequest
utwórz instancję nowego obiektu FormData
z elementu formularza msg-form
:
const formData = new FormData(document.getElementById('msg-form'));
Następnie zastąp wartość parametru body
zmienną formData
.
Zapisz skrypt i odśwież stronę. Wypełnij formularz (pola Imię i Wiadomość) na stronie, a potem kliknij prośbę POST. Sprawdź zawartość formularza wyświetlaną na stronie.
Wyjaśnienie
Konstruktor FormData
może przyjmować element HTML form
i tworzyć obiekt FormData
. Ten obiekt jest wypełniany kluczami i wartościami formularza.
Więcej informacji
Uruchamianie serwera echa bez CORS
Zatrzymaj poprzedni serwer echo (naciskając ctrl+c
w wierszu poleceń) i uruchom nowy serwer echo z katalogu fetch-lab-api/app/
, wykonując to polecenie:
node echo-servers/no-cors-server.js
To polecenie konfiguruje kolejny prosty serwer echo, tym razem na porcie localhost:5001/
. Ten serwer nie jest jednak skonfigurowany do przyjmowania żądań z innych domen.
Pobieranie z nowego serwera
Nowy serwer działa już pod adresem localhost:5001/
, więc możemy wysłać do niego żądanie pobrania.
Zaktualizuj funkcję postRequest
, aby pobierała dane z localhost:5001/
zamiast z localhost:5000/
. Po zaktualizowaniu kodu zapisz plik, odśwież stronę, a następnie kliknij POST Request (Żądanie POST).
W konsoli powinien pojawić się błąd wskazujący, że żądanie z innej domeny zostało zablokowane, ponieważ brakuje nagłówka CORS Access-Control-Allow-Origin
.
Zaktualizuj fetch
w funkcji postRequest
za pomocą tego kodu, który używa trybu no-cors (jak sugeruje dziennik błędów) i usuwa wywołania funkcji validateResponse
i readResponseAsText
(wyjaśnienie poniżej):
function postRequest() {
const formData = new FormData(document.getElementById('msg-form'));
fetch('http://localhost:5001/', {
method: 'POST',
body: formData,
mode: 'no-cors'
})
.then(logResult)
.catch(logError);
}
Zapisz skrypt i odśwież stronę. Następnie wypełnij formularz wiadomości i kliknij POST Request (Wyślij prośbę).
Obserwuj obiekt odpowiedzi zalogowany w konsoli.
Wyjaśnienie
Funkcje Fetch i XMLHttpRequest są zgodne z zasadami dotyczącymi tej samej domeny. Oznacza to, że przeglądarki ograniczają żądania HTTP z innych domen w skryptach. Żądanie z innej domeny występuje, gdy jedna domena (np. http://foo.com/
) żąda zasobu z innej domeny (np. http://bar.com/
).
Uwaga: ograniczenia dotyczące żądań z innych domen często są przyczyną nieporozumień. Wiele zasobów, takich jak obrazy, arkusze stylów i skrypty, jest pobieranych z różnych domen (czyli z innych źródeł). Są to jednak wyjątki od zasady dotyczącej tej samej domeny. Żądania z innej domeny są nadal ograniczone w przypadku skryptów.
Ponieważ serwer naszej aplikacji ma inny numer portu niż oba serwery echo, żądania wysyłane do dowolnego z nich są traktowane jako żądania z innego źródła. Pierwszy serwer echo działający na localhost:5000/
jest jednak skonfigurowany tak, aby obsługiwać CORS (możesz otworzyć echo-servers/cors-server.js
i sprawdzić konfigurację). Nowy serwer echo działający na localhost:5001/
nie jest (dlatego otrzymujemy błąd).
Użycie mode: no-cors
umożliwia pobranie nieprzezroczystej odpowiedzi. Pozwala to uzyskać odpowiedź, ale uniemożliwia dostęp do niej za pomocą JavaScriptu (dlatego nie możemy używać validateResponse
, readResponseAsText
ani showResponse
). Odpowiedź może być nadal wykorzystywana przez inne interfejsy API lub buforowana przez service worker.
Modyfikowanie nagłówków żądań
Interfejs Fetch umożliwia też modyfikowanie nagłówków żądań. Zatrzymaj serwer echa localhost:5001
(bez CORS) i uruchom ponownie serwer echa localhost:5000
(CORS) z sekcji 6:
node echo-servers/cors-server.js
Przywróć poprzednią wersję funkcji postRequest
, która pobiera dane z localhost:5000/
:
function postRequest() {
const formData = new FormData(document.getElementById('msg-form'));
fetch('http://localhost:5000/', {
method: 'POST',
body: formData
})
.then(validateResponse)
.then(readResponseAsText)
.then(showText)
.catch(logError);
}
Teraz użyj interfejsu nagłówka, aby utworzyć obiekt nagłówków w funkcji postRequest
o nazwie messageHeaders
z nagłówkiem Content-Type
równym application/json
.
Następnie ustaw właściwość headers
obiektu init
na zmienną messageHeaders
.
Zaktualizuj właściwość body
, aby była ciągiem znaków w formacie obiektu JSON, np.:
JSON.stringify({ lab: 'fetch', status: 'fun' })
Po zaktualizowaniu kodu zapisz plik i odśwież stronę. Następnie kliknij POST Request (WYŚLIJ PROŚBĘ).
Zauważ, że w odpowiedzi na żądanie wartość parametru Content-Type
wynosi teraz application/json
(wcześniej było to multipart/form-data
).
Teraz dodaj niestandardowy Content-Length
nagłówek do obiektu messageHeaders
i nadaj żądaniu dowolny rozmiar.
Po zaktualizowaniu kodu zapisz plik, odśwież stronę i kliknij POST Request (Żądanie POST). Zauważ, że ten nagłówek nie jest modyfikowany w odpowiedzi na żądanie.
Wyjaśnienie
Interfejs nagłówka umożliwia tworzenie i modyfikowanie obiektów nagłówków. Niektóre nagłówki, np. Content-Type
, można modyfikować za pomocą pobierania. Inne, np. Content-Length
, są chronione i nie można ich modyfikować (ze względów bezpieczeństwa).
Ustawianie niestandardowych nagłówków żądań
Interfejs Fetch obsługuje ustawianie niestandardowych nagłówków.
Usuń nagłówek Content-Length
z obiektu messageHeaders
w funkcji postRequest
. Dodaj nagłówek niestandardowy X-Custom
z dowolną wartością (np. „X-CUSTOM': 'hello world'
”).
Zapisz skrypt, odśwież stronę, a potem kliknij POST Request (Wyślij żądanie POST).
Powinno być widoczne, że w odpowiedzi na żądanie znajduje się dodana przez Ciebie właściwość X-Custom
.
Teraz dodaj nagłówek Y-Custom
do obiektu Headers. Zapisz skrypt, odśwież stronę i kliknij POST Request (Żądanie POST).
W konsoli powinien pojawić się błąd podobny do tego:
Fetch API cannot load http://localhost:5000/. Request header field y-custom is not allowed by Access-Control-Allow-Headers in preflight response.
Wyjaśnienie
Podobnie jak w przypadku żądań dotyczących zasobów z innej domeny, niestandardowe nagłówki muszą być obsługiwane przez serwer, z którego pochodzi żądany zasób. W tym przykładzie nasz serwer echo jest skonfigurowany tak, aby akceptować nagłówek X-Custom
, ale nie nagłówek Y-Custom
(możesz otworzyć echo-servers/cors-server.js
i poszukać Access-Control-Allow-Headers
, aby się o tym przekonać). Za każdym razem, gdy ustawiany jest niestandardowy nagłówek, przeglądarka przeprowadza kontrolę wstępną. Oznacza to, że przeglądarka najpierw wysyła do serwera żądanie OPTIONS, aby określić, jakie metody i nagłówki HTTP są dozwolone przez serwer. Jeśli serwer jest skonfigurowany tak, aby akceptować metodę i nagłówki pierwotnego żądania, żądanie jest wysyłane. W przeciwnym razie występuje błąd.
Więcej informacji
Kod rozwiązania
Aby uzyskać kopię działającego kodu, otwórz folder solution.
Wiesz już, jak korzystać z interfejsu Fetch API.
Zasoby
Aby zobaczyć wszystkie codelaby w ramach szkolenia PWA, zapoznaj się z codelabem wprowadzającym.