Utmaningar
Nedan finns sex frågor om routing, lazy loading och opålitliga nätverk. Frågorna bygger på veckans artiklar Routing i en SPA, Lazy loading och Online, offline och Lie-Fi.
Varje fråga har ett antal alternativ att välja bland. Klicka på “Visa lösning” för att se svaret och förklaringen.
Om du är osäker kan du alltid testa själv med npm run build och npm run serve, och följa vad som
händer i flikarna Network och Application i webbläsarens utvecklarverktyg, eller ta upp frågan på
en handledning.
Fråga 16-01: pushState()
Section titled “Fråga 16-01: pushState()”Sidan visas på http://localhost:4173/?size=4x4. Koden kör
history.pushState(null, '', '?size=2x2'). Vad händer?
- Webbläsaren hämtar
?size=2x2från servern och laddar om sidan. - URL:en byts och en post läggs till i historiken. Inget hämtas och ingen händelse utlöses.
- URL:en byts och
popstateutlöses, så att sidan kan visa 2 × 2. - URL:en byts, men bakåtknappen tar användaren till sidan före
?size=4x4.
Lösningsförslag
Section titled “Lösningsförslag”Svaret är alternativ 2. pushState() byter URL:en och lägger till en post i historiken, men gör
ingenting mer. Det är din kod som ändrar det som visas.
popstate utlöses inte av pushState(), bara när användaren går bakåt eller framåt mellan
posterna. Alternativ 1 beskriver vad location.search = '?size=2x2' gör. Alternativ 4 stämmer inte:
bakåtknappen tar användaren tillbaka till ?size=4x4, posten före den nya.
Fråga 16-02: Service workern på en djup sökväg
Section titled “Fråga 16-02: Service workern på en djup sökväg”En SPA använder sökvägar för routing och registrerar service workern med
navigator.serviceWorker.register('./sw.js'). Användaren öppnar http://localhost:4173/history/2
direkt. Servern svarar med index.html för alla sökvägar den inte hittar. Vad händer?
- Service workern registreras från
/sw.js, eftersom relativa URL:er räknas från roten. - Webbläsaren försöker registrera
/history/sw.js, får HTML tillbaka och registreringen misslyckas. - Service workern registreras, men med räckvidden
/history/. - Inget, service workers fungerar inte med History API.
Lösningsförslag
Section titled “Lösningsförslag”Svaret är alternativ 2. ./sw.js räknas från sidans URL, /history/2, och blir /history/sw.js.
Servern har ingen sådan fil och svarar med index.html. Webbläsaren vägrar att registrera HTML som
service worker, med felet unsupported MIME type (‘text/html’).
Alternativ 3 hade varit fallet om /history/sw.js verkligen fanns. Med sökvägar för routing behöver
allt som sidan pekar ut räknas från roten, till exempel register('/sw.js'). Med query stringen
uppstår inte problemet.
Fråga 16-03: Ett element som inte är definierat
Section titled “Fråga 16-03: Ett element som inte är definierat”index.html innehåller <game-history hidden></game-history>. Komponenten laddas med import()
när användaren klickar:
historyButton.addEventListener('click', () => { import('./components/game-history/index.js') historyElement.showHistory(loadHistory())})Vad händer första gången användaren klickar?
- Historiken visas, eftersom
import()laddar modulen innan nästa rad körs. - Ett
SyntaxError, eftersomimport()bara får användas överst i en fil. - Ett
TypeError, eftersomshowHistory()inte finns förräncustomElements.define()har körts. - Inget, eftersom webbläsaren tar bort element den inte känner igen.
Lösningsförslag
Section titled “Lösningsförslag”Svaret är alternativ 3. import() startar hämtningen och ger ett promise, men lyssnaren väntar inte
på det. När nästa rad körs är <game-history> fortfarande ett vanligt HTMLElement, utan
showHistory(). När define() har körts uppgraderas elementet, och först då finns metoden.
Lösningen är att göra lyssnaren async och vänta med await import(...), i ett try som fångar
fel. import() får stå var som helst, så alternativ 2 stämmer inte, och okända element finns kvar i
DOM:en, så alternativ 4 stämmer inte heller.
Fråga 16-04: Offline för första gången
Section titled “Fråga 16-04: Offline för första gången”Ett PWD laddar underapplikationerna med import() när användaren klickar på en ikon. Service workern
cachar filerna under /assets/ med cache first, under körning. Användaren har öppnat PWD:et flera
gånger, och Memory en gång, men aldrig Messages. Nu är användaren offline. Vad händer?
- Båda går att öppna, eftersom service workern cachar alla filer under
/assets/. - Ingen av dem går att öppna, eftersom
import()inte går genom service workern. - Memory går att öppna, men
import()för Messages blir rejected med ettTypeError. - Memory går att öppna, och Messages öppnas men visar en tom sida.
Lösningsförslag
Section titled “Lösningsförslag”Svaret är alternativ 3. Memorys modul hämtades genom service workern när användaren öppnade den, och
finns i cachen. Messages modul har aldrig hämtats, och finns inte. Offline misslyckas import() med
Failed to fetch dynamically imported module, och din kod behöver fånga felet och berätta det för
användaren.
Service workern kan bara cacha det den har sett, så alternativ 1 stämmer inte. Det är vad kravet i B3 säger: underapplikationer som har öppnats minst en gång ska fungera offline.
Fråga 16-05: navigator.onLine
Section titled “Fråga 16-05: navigator.onLine”navigator.onLine är true. Vad vet du?
- Att servern går att nå.
- Att enheten har en fungerande internetförbindelse.
- Att webbläsaren anser att enheten är ansluten till ett nätverk.
- Att det senaste
fetch()-anropet lyckades.
Lösningsförslag
Section titled “Lösningsförslag”Svaret är alternativ 3. Webbläsaren vet om enheten är ansluten till ett nätverk, till exempel ett
wifi, inte om det nätverket når internet eller din server. Ett hotell-wifi utan inloggning, eller en
telefon med Lie-Fi, ger true.
Använd värdet som en ledtråd till användaren, men bygg felhanteringen på det som faktiskt händer: ett
fetch() som misslyckas eller en WebSocket som stängs.
Fråga 16-06: Timeouten
Section titled “Fråga 16-06: Timeouten”En komponent hämtar data med
fetch(url, { signal: AbortSignal.any([this.#abortController.signal, AbortSignal.timeout(8000)]) }).
Servern svarar inte på tio sekunder. Med vilket fel blir promiset rejected, och vad ska komponenten
göra?
AbortError, och komponenten ska ignorera det.TimeoutError, och komponenten ska berätta för användaren att servern inte svarar.TypeError, och komponenten ska försöka igen direkt.- Inget fel,
fetch()väntar tills servern svarar.
Lösningsförslag
Section titled “Lösningsförslag”Svaret är alternativ 2. Signalen från AbortSignal.timeout() avbryter efter åtta sekunder, och
fetch() blir rejected med ett DOMException som heter TimeoutError. Det betyder att servern inte
svarade, och det ska användaren få veta.
AbortError hade det blivit om komponentens egen AbortController hade avbrutit, till exempel för
att en nyare förfrågan ersatte den, och det ska ignoreras. Alternativ 4 beskriver fetch() utan
timeout, som kan vänta i minuter.