Hoppa till innehåll

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.

Sidan visas på http://localhost:4173/?size=4x4. Koden kör history.pushState(null, '', '?size=2x2'). Vad händer?

  1. Webbläsaren hämtar ?size=2x2 från servern och laddar om sidan.
  2. URL:en byts och en post läggs till i historiken. Inget hämtas och ingen händelse utlöses.
  3. URL:en byts och popstate utlöses, så att sidan kan visa 2 × 2.
  4. URL:en byts, men bakåtknappen tar användaren till sidan före ?size=4x4.

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?

  1. Service workern registreras från /sw.js, eftersom relativa URL:er räknas från roten.
  2. Webbläsaren försöker registrera /history/sw.js, får HTML tillbaka och registreringen misslyckas.
  3. Service workern registreras, men med räckvidden /history/.
  4. Inget, service workers fungerar inte med History API.

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?

  1. Historiken visas, eftersom import() laddar modulen innan nästa rad körs.
  2. Ett SyntaxError, eftersom import() bara får användas överst i en fil.
  3. Ett TypeError, eftersom showHistory() inte finns förrän customElements.define() har körts.
  4. Inget, eftersom webbläsaren tar bort element den inte känner igen.

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?

  1. Båda går att öppna, eftersom service workern cachar alla filer under /assets/.
  2. Ingen av dem går att öppna, eftersom import() inte går genom service workern.
  3. Memory går att öppna, men import() för Messages blir rejected med ett TypeError.
  4. Memory går att öppna, och Messages öppnas men visar en tom sida.

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.

navigator.onLine är true. Vad vet du?

  1. Att servern går att nå.
  2. Att enheten har en fungerande internetförbindelse.
  3. Att webbläsaren anser att enheten är ansluten till ett nätverk.
  4. Att det senaste fetch()-anropet lyckades.

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.

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?

  1. AbortError, och komponenten ska ignorera det.
  2. TimeoutError, och komponenten ska berätta för användaren att servern inte svarar.
  3. TypeError, och komponenten ska försöka igen direkt.
  4. Inget fel, fetch() väntar tills servern svarar.

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.

CCBY