Mestr avancerede Fetch API-teknikker: aflyt anmodninger og implementer responscache for optimal ydeevne. Lær bedste praksis for globale applikationer.
Avanceret Fetch API: Anmodningsaflytning og Responscaching
Fetch API er blevet standarden for at lave netværksanmodninger i moderne JavaScript. Mens grundlæggende brug er ligetil, kræver det at frigøre dets fulde potentiale en forståelse af avancerede teknikker som anmodningsaflytning og responscaching. Denne artikel vil udforske disse koncepter i detaljer og give praktiske eksempler og bedste praksis for at bygge højtydende, globalt tilgængelige webapplikationer.
Forståelse af Fetch API
Fetch API'et giver en kraftfuld og fleksibel grænseflade til at hente ressourcer på tværs af netværket. Det bruger Promises, hvilket gør asynkrone operationer lettere at håndtere og ræsonnere om. Før vi dykker ned i avancerede emner, lad os kort gennemgå det grundlæggende:
Grundlæggende Fetch-brug
En simpel Fetch-anmodning ser sådan ud:
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Data:', data);
})
.catch(error => {
console.error('Fetch error:', error);
});
Denne kode henter data fra den angivne URL, tjekker for HTTP-fejl, parser svaret som JSON og logger dataene til konsollen. Fejlhåndtering er afgørende for at sikre en robust applikation.
Anmodningsaflytning
Anmodningsaflytning involverer at modificere eller observere netværksanmodninger, før de sendes til serveren. Dette kan være nyttigt til forskellige formål, herunder:
- Tilføjelse af godkendelsesheadere
- Transformering af anmodningsdata
- Logning af anmodninger til debugging
- Mocking af API-svar under udvikling
Anmodningsaflytning opnås typisk ved hjælp af en Service Worker, der fungerer som en proxy mellem webapplikationen og netværket.
Service Workers: Grundlaget for Aflytning
En Service Worker er en JavaScript-fil, der kører i baggrunden, adskilt fra browserens hovedtråd. Den kan aflytte netværksanmodninger, cache svar og levere offline-funktionalitet. For at bruge en Service Worker skal du først registrere den:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js')
.then(registration => {
console.log('Service Worker registered with scope:', registration.scope);
})
.catch(error => {
console.error('Service Worker registration failed:', error);
});
}
Denne kode tjekker, om browseren understøtter Service Workers, og registrerer service-worker.js
-filen. Omfanget (scope) definerer, hvilke URL'er Service Workeren vil kontrollere.
Implementering af Anmodningsaflytning
Inde i service-worker.js
-filen kan du aflytte anmodninger ved hjælp af fetch
-eventet:
self.addEventListener('fetch', event => {
// Intercept all fetch requests
event.respondWith(
new Promise(resolve => {
// Clone the request to avoid modifying the original
const req = event.request.clone();
// Modify the request (e.g., add an authentication header)
const headers = new Headers(req.headers);
headers.append('Authorization', 'Bearer your_api_key');
const modifiedReq = new Request(req.url, {
method: req.method,
headers: headers,
body: req.body,
mode: 'cors',
credentials: req.credentials,
cache: req.cache,
redirect: req.redirect,
referrer: req.referrer,
integrity: req.integrity
});
// Make the modified request
fetch(modifiedReq)
.then(response => resolve(response))
.catch(error => {
console.error('Fetch error in Service Worker:', error);
// Optionally, return a default response or error page
resolve(new Response('Offline', { status: 503, statusText: 'Service Unavailable' }));
});
})
);
});
Denne kode aflytter hver fetch
-anmodning, kloner den, tilføjer en Authorization
-header og foretager derefter den modificerede anmodning. Metoden event.respondWith()
fortæller browseren, hvordan den skal håndtere anmodningen. Det er afgørende at klone anmodningen; ellers vil du modificere den oprindelige anmodning, hvilket kan føre til uventet adfærd.
Den sikrer også, at alle oprindelige anmodningsindstillinger videresendes for at sikre kompatibilitet. Bemærk fejlhåndteringen: det er vigtigt at have en fallback, hvis fetch mislykkes (f.eks. når man er offline).
Eksempel: Tilføjelse af Godkendelsesheadere
En almindelig anvendelse af anmodningsaflytning er at tilføje godkendelsesheadere til API-anmodninger. Dette sikrer, at kun autoriserede brugere kan få adgang til beskyttede ressourcer.
self.addEventListener('fetch', event => {
if (event.request.url.startsWith('https://api.example.com')) {
event.respondWith(
new Promise(resolve => {
const req = event.request.clone();
const headers = new Headers(req.headers);
// Replace with actual authentication logic (e.g., retrieving token from local storage)
const token = localStorage.getItem('api_token');
if (token) {
headers.append('Authorization', `Bearer ${token}`);
} else {
console.warn("No API token found, request may fail.");
}
const modifiedReq = new Request(req.url, {
method: req.method,
headers: headers,
body: req.body,
mode: 'cors',
credentials: req.credentials,
cache: req.cache,
redirect: req.redirect,
referrer: req.referrer,
integrity: req.integrity
});
fetch(modifiedReq)
.then(response => resolve(response))
.catch(error => {
console.error('Fetch error in Service Worker:', error);
resolve(new Response('Offline', { status: 503, statusText: 'Service Unavailable' }));
});
})
);
} else {
// Let the browser handle the request as usual
event.respondWith(fetch(event.request));
}
});
Denne kode tilføjer en Authorization
-header til anmodninger, der starter med https://api.example.com
. Den henter API-tokenet fra local storage. Det er afgørende at implementere korrekt tokenhåndtering og sikkerhedsforanstaltninger, såsom HTTPS og sikker lagring.
Eksempel: Transformering af Anmodningsdata
Anmodningsaflytning kan også bruges til at transformere anmodningsdata, før de sendes til serveren. For eksempel vil du måske konvertere data til et specifikt format eller tilføje yderligere parametre.
self.addEventListener('fetch', event => {
if (event.request.url.includes('/submit-form')) {
event.respondWith(
new Promise(resolve => {
const req = event.request.clone();
req.text().then(body => {
try {
const parsedBody = JSON.parse(body);
// Transform the data (e.g., add a timestamp)
parsedBody.timestamp = new Date().toISOString();
// Convert the transformed data back to JSON
const transformedBody = JSON.stringify(parsedBody);
const modifiedReq = new Request(req.url, {
method: req.method,
headers: req.headers,
body: transformedBody,
mode: 'cors',
credentials: req.credentials,
cache: req.cache,
redirect: req.redirect,
referrer: req.referrer,
integrity: req.integrity
});
fetch(modifiedReq)
.then(response => resolve(response))
.catch(error => {
console.error('Fetch error in Service Worker:', error);
resolve(new Response('Offline', { status: 503, statusText: 'Service Unavailable' }));
});
} catch (error) {
console.error("Error parsing request body:", error);
resolve(fetch(event.request)); // Fallback to original request
}
});
})
);
} else {
event.respondWith(fetch(event.request));
}
});
Denne kode aflytter anmodninger til /submit-form
, parser anmodningens body som JSON, tilføjer et tidsstempel og sender derefter de transformerede data til serveren. Fejlhåndtering er essentiel for at sikre, at applikationen ikke går ned, hvis anmodningens body ikke er gyldig JSON.
Responscaching
Responscaching involverer at gemme svar fra API-anmodninger i browserens cache. Dette kan forbedre ydeevnen betydeligt ved at reducere antallet af netværksanmodninger. Når et cachet svar er tilgængeligt, kan browseren levere det direkte fra cachen uden at skulle lave en ny anmodning til serveren.
Fordele ved Responscaching
- Forbedret Ydeevne: Hurtigere indlæsningstider og en mere responsiv brugeroplevelse.
- Reduceret Båndbreddeforbrug: Mindre data overføres over netværket, hvilket sparer båndbredde for både brugeren og serveren.
- Offline Funktionalitet: Cachede svar kan leveres, selv når brugeren er offline, hvilket giver en problemfri oplevelse.
- Omkostningsbesparelser: Lavere båndbreddeforbrug betyder lavere omkostninger for både brugere og tjenesteudbydere, især i regioner med dyre eller begrænsede dataplaner.
Implementering af Responscaching med Service Workers
Service Workers giver en kraftfuld mekanisme til implementering af responscaching. Du kan bruge Cache
API'et til at gemme og hente svar.
const cacheName = 'my-app-cache-v1';
const cacheableUrls = [
'/',
'/index.html',
'/styles.css',
'/script.js',
'https://api.example.com/data'
];
// Install event: Cache static assets
self.addEventListener('install', event => {
event.waitUntil(
caches.open(cacheName)
.then(cache => {
console.log('Caching app shell');
return cache.addAll(cacheableUrls);
})
);
});
// Activate event: Clean up old caches
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.filter(name => name !== cacheName)
.map(name => caches.delete(name))
);
})
);
});
// Fetch event: Serve cached responses or fetch from the network
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// Cache hit - return response
if (response) {
return response;
}
// Not in cache - fetch from network
return fetch(event.request).then(
response => {
// Check if we received a valid response
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// Clone the response (because it's a stream and can only be consumed once)
const responseToCache = response.clone();
caches.open(cacheName)
.then(cache => {
cache.put(event.request, responseToCache);
});
return response;
}
).catch(error => {
// Handle network error
console.error("Fetch failed:", error);
// Optionally, provide a fallback response (e.g., offline page)
return caches.match('/offline.html');
});
})
);
});
Denne kode cacher statiske aktiver under install-eventet og leverer cachede svar under fetch-eventet. Hvis et svar ikke findes i cachen, hentes det fra netværket, caches og returneres derefter. activate
-eventet bruges til at rydde op i gamle caches, når Service Workeren opdateres. Denne tilgang sikrer også, at kun gyldige svar (status 200 og type 'basic') caches.
Cachestrategier
Der er flere forskellige cachestrategier, du kan bruge, afhængigt af din applikations behov:
- Cache-First: Prøv først at levere svaret fra cachen. Hvis det ikke findes, hentes det fra netværket og caches. Dette er godt for statiske aktiver og ressourcer, der ikke ændrer sig ofte.
- Network-First: Prøv først at hente svaret fra netværket. Hvis det mislykkes, leveres det fra cachen. Dette er godt for dynamiske data, der skal være opdaterede.
- Cache, derefter Netværk: Lever svaret fra cachen med det samme, og opdater derefter cachen med den seneste version fra netværket. Dette giver en hurtig indledende indlæsning og sikrer, at brugeren altid har de seneste data (til sidst).
- Stale-While-Revalidate: Returner et cachet svar med det samme, mens du også tjekker netværket for en opdateret version. Opdater cachen i baggrunden, hvis en nyere version er tilgængelig. Ligner "Cache, derefter Netværk", men giver en mere problemfri brugeroplevelse.
Valget af cachestrategi afhænger af de specifikke krav til din applikation. Overvej faktorer som hyppigheden af opdateringer, vigtigheden af friskhed og den tilgængelige båndbredde.
Eksempel: Caching af API-svar
Her er et eksempel på caching af API-svar ved hjælp af Cache-First-strategien:
self.addEventListener('fetch', event => {
if (event.request.url.startsWith('https://api.example.com')) {
event.respondWith(
caches.match(event.request)
.then(response => {
// Cache hit - return response
if (response) {
return response;
}
// Not in cache - fetch from network
return fetch(event.request).then(
response => {
// Check if we received a valid response
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// Clone the response (because it's a stream and can only be consumed once)
const responseToCache = response.clone();
caches.open(cacheName)
.then(cache => {
cache.put(event.request, responseToCache);
});
return response;
}
);
})
);
} else {
// Let the browser handle the request as usual
event.respondWith(fetch(event.request));
}
});
Denne kode cacher API-svar fra https://api.example.com
. Når en anmodning foretages, tjekker Service Workeren først, om svaret allerede er i cachen. Hvis det er tilfældet, returneres det cachede svar. Hvis ikke, foretages anmodningen til netværket, og svaret caches, før det returneres.
Avancerede Overvejelser
Cache-invalidering
En af de største udfordringer med caching er cache-invalidering. Når data ændres på serveren, skal du sikre dig, at cachen opdateres. Der er flere strategier for cache-invalidering:
- Cache Busting: Tilføj et versionsnummer eller tidsstempel til ressourcens URL. Når ressourcen ændres, ændres URL'en, og browseren vil hente den nye version.
- Tidsbaseret Udløb: Indstil en maksimal alder for cachede svar. Efter udløbstiden vil browseren hente en ny version fra serveren. Brug
Cache-Control
-headeren til at angive den maksimale alder. - Manuel Invalidering: Brug
caches.delete()
-metoden til manuelt at fjerne cachede svar. Dette kan udløses af en server-side begivenhed eller en brugerhandling. - WebSockets til Real-time Opdateringer: Brug WebSockets til at pushe opdateringer fra serveren til klienten og invalidere cachen, når det er nødvendigt.
Content Delivery Networks (CDN'er)
Content Delivery Networks (CDN'er) er distribuerede netværk af servere, der cacher indhold tættere på brugerne. Brug af et CDN kan forbedre ydeevnen betydeligt for brugere over hele verden ved at reducere latenstid og båndbreddeforbrug. Populære CDN-udbydere inkluderer Cloudflare, Amazon CloudFront og Akamai. Når du integrerer med CDN'er, skal du sikre, at Cache-Control
-headere er korrekt konfigureret for optimal caching-adfærd.
Sikkerhedsovervejelser
Når du implementerer anmodningsaflytning og responscaching, er det vigtigt at overveje sikkerhedsmæssige konsekvenser:
- HTTPS: Brug altid HTTPS til at beskytte data under overførsel.
- CORS: Konfigurer Cross-Origin Resource Sharing (CORS) korrekt for at forhindre uautoriseret adgang til ressourcer.
- Datasanering: Saner brugerinput for at forhindre cross-site scripting (XSS) angreb.
- Sikker Opbevaring: Opbevar følsomme data, såsom API-nøgler og tokens, sikkert (f.eks. ved hjælp af HTTPS-only cookies eller en sikker lager-API).
- Subresource Integrity (SRI): Brug SRI til at sikre, at ressourcer hentet fra tredjeparts-CDN'er ikke er blevet manipuleret.
Debugging af Service Workers
Debugging af Service Workers kan være udfordrende, men browserens udviklerværktøjer giver flere funktioner, der kan hjælpe:
- Application-faneblad: Application-fanebladet i Chrome DevTools giver information om Service Workers, herunder deres status, scope og cache-lager.
- Konsollogning: Brug
console.log()
-udsagn til at logge information om Service Worker-aktivitet. - Breakpoints: Sæt breakpoints i Service Worker-koden for at gennemgå eksekveringen trin for trin og inspicere variabler.
- Opdater ved Genindlæsning: Aktivér "Update on reload" i Application-fanebladet for at sikre, at Service Workeren opdateres, hver gang du genindlæser siden.
- Afregistrer Service Worker: Brug "Unregister"-knappen i Application-fanebladet til at afregistrere Service Workeren. Dette kan være nyttigt til fejlfinding eller til at starte fra en ren tavle.
Konklusion
Anmodningsaflytning og responscaching er kraftfulde teknikker, der markant kan forbedre ydeevnen og brugeroplevelsen i webapplikationer. Ved at bruge Service Workers kan du aflytte netværksanmodninger, modificere dem efter behov og cache svar for offline-funktionalitet og hurtigere indlæsningstider. Når de implementeres korrekt, kan disse teknikker hjælpe dig med at bygge højtydende, globalt tilgængelige webapplikationer, der giver en problemfri brugeroplevelse, selv under udfordrende netværksforhold. Overvej de forskellige netværksforhold og dataomkostninger, som brugere verden over står over for, når du implementerer disse teknikker for at sikre optimal tilgængelighed og inklusivitet. Prioriter altid sikkerhed for at beskytte følsomme data og forhindre sårbarheder.
Ved at mestre disse avancerede Fetch API-teknikker kan du tage dine webudviklingsfærdigheder til det næste niveau og bygge virkelig exceptionelle webapplikationer.