`Service Worker` i JavaScript

`Service Worker` i JavaScript

Denne artikkelen forklarer konseptet med Service Worker i JavaScript.

Vi vil forklare trinn for trinn fra grunnleggende om Service Worker til praktisk cache-kontroll.

YouTube Video

offline.html
 1<!DOCTYPE html>
 2<html lang="en">
 3<head>
 4  <meta charset="UTF-8">
 5  <title>Offline</title>
 6</head>
 7<body>
 8  <h1>You are offline</h1>
 9  <p>This is the offline fallback page.</p>
10</body>
11</html>
style.css
1body {
2  font-family: sans-serif;
3  background-color: #f0f0f0;
4  padding: 20px;
5}
6h1 {
7  color: #333;
8}
javascript-service-worker.html
  1<!DOCTYPE html>
  2<html lang="en">
  3<head>
  4  <meta charset="UTF-8">
  5  <title>JavaScript &amp; HTML</title>
  6  <style>
  7    * {
  8        box-sizing: border-box;
  9    }
 10
 11    body {
 12        margin: 0;
 13        padding: 1em;
 14        padding-bottom: 10em;
 15        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
 16        background-color: #f7f9fc;
 17        color: #333;
 18        line-height: 1.6;
 19    }
 20
 21    .container {
 22        max-width: 800px;
 23        margin: 0 auto;
 24        padding: 1em;
 25        background-color: #ffffff;
 26        border: 1px solid #ccc;
 27        border-radius: 10px;
 28        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
 29    }
 30
 31    .container-flex {
 32        display: flex;
 33        flex-wrap: wrap;
 34        gap: 2em;
 35        max-width: 1000px;
 36        margin: 0 auto;
 37        padding: 1em;
 38        background-color: #ffffff;
 39        border: 1px solid #ccc;
 40        border-radius: 10px;
 41        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
 42    }
 43
 44    .left-column, .right-column {
 45        flex: 1 1 200px;
 46        min-width: 200px;
 47    }
 48
 49    h1, h2 {
 50        font-size: 1.2rem;
 51        color: #007bff;
 52        margin-top: 0.5em;
 53        margin-bottom: 0.5em;
 54        border-left: 5px solid #007bff;
 55        padding-left: 0.6em;
 56        background-color: #e9f2ff;
 57    }
 58
 59    button {
 60        display: block;
 61        margin: 1em auto;
 62        padding: 0.75em 1.5em;
 63        font-size: 1rem;
 64        background-color: #007bff;
 65        color: white;
 66        border: none;
 67        border-radius: 6px;
 68        cursor: pointer;
 69        transition: background-color 0.3s ease;
 70    }
 71
 72    button:hover {
 73        background-color: #0056b3;
 74    }
 75
 76    #output {
 77        margin-top: 1em;
 78        background-color: #1e1e1e;
 79        color: #0f0;
 80        padding: 1em;
 81        border-radius: 8px;
 82        min-height: 200px;
 83        font-family: Consolas, monospace;
 84        font-size: 0.95rem;
 85        overflow-y: auto;
 86        white-space: pre-wrap;
 87    }
 88
 89    .highlight {
 90        outline: 3px solid #ffc107; /* yellow border */
 91        background-color: #fff8e1;  /* soft yellow background */
 92        transition: background-color 0.3s ease, outline 0.3s ease;
 93    }
 94
 95    .active {
 96        background-color: #28a745; /* green background */
 97        color: #fff;
 98        box-shadow: 0 0 10px rgba(40, 167, 69, 0.5);
 99        transition: background-color 0.3s ease, box-shadow 0.3s ease;
100    }
101  </style>
102</head>
103<body>
104    <div class="container">
105        <h1>JavaScript Console</h1>
106        <button id="executeBtn">Execute</button>
107        <div id="output"></div>
108    </div>
109
110    <div class="container">
111        <h2>HTML Sample</h2>
112        <button id="fetchBtn">Fetch Test</button>
113    </div>
114
115    <script>
116        // Override console.log to display messages in the #output element
117        (function () {
118            // Override console.log
119            const originalLog = console.log;
120            console.log = function (...args) {
121                originalLog.apply(console, args);
122                const message = document.createElement('div');
123                message.textContent = args.map(String).join(' ');
124                output.appendChild(message);
125            };
126
127            // Override console.error
128            const originalError = console.error;
129            console.error = function (...args) {
130                originalError.apply(console, args);
131                const message = document.createElement('div');
132                message.textContent = args.map(String).join(' ');
133                message.style.color = 'red'; // Color error messages red
134                output.appendChild(message);
135            };
136        })();
137
138        document.getElementById('executeBtn').addEventListener('click', () => {
139            // Prevent multiple loads
140            if (document.getElementById('externalScript')) return;
141
142            const script = document.createElement('script');
143            script.src = 'javascript-service-worker.js';
144            script.id = 'externalScript';
145            //script.onload = () => console.log('javascript-service-worker.js loaded and executed.');
146            //script.onerror = () => console.log('Failed to load javascript-service-worker.js.');
147            document.body.appendChild(script);
148        });
149    </script>
150</body>
151</html>

Service Worker i JavaScript

Service Worker er en JavaScript-funksjon som står mellom nettleseren og nettverket, og muliggjør caching av forespørsler og offline-støtte. Det er en kjerneteknologi i PWA-er (Progressive Web Apps) og gir en app-lignende opplevelse til nettapplikasjoner.

Hva er en Service Worker?

Service Worker er en JavaScript-fil som kjører i nettleserens bakgrunnstråd. Den kjører på en separat tråd fra siden, har ikke tilgang til brukergrensesnittet, men kan fange opp nettverksforespørsler, håndtere caching og motta push-varsler.

Nøkkelfunksjonene til en Service Worker inkluderer følgende:.

  • Den fungerer kun over HTTPS, bortsett fra på localhost.
  • Den bruker et asynkront API basert på Promise.
  • Den er hendelsesdrevet og bruker hendelser som install, activate, fetch og push.

Registrering av en Service Worker

Først skal vi skrive koden for å registrere en Service Worker i nettleseren.

 1if ('serviceWorker' in navigator) {
 2    window.addEventListener('load', () => {
 3        navigator.serviceWorker.register('/sw.js')
 4        .then(registration => {
 5            console.log(
 6                'Service Worker registered with scope:',
 7                registration.scope
 8            );
 9        })
10        .catch(error => {
11            console.error('Service Worker registration failed:', error);
12        });
13    });
14}

Forklaring

  • Bruk navigator.serviceWorker.register() for å registrere /sw.js (Service Worker-filen).
  • Du kan bruke then for håndtering av suksess og catch for feil ved registrering.
  • registration.scope representerer banen (omfanget) som er påvirket av Service Worker.
  • Som standard er omfanget katalogen der den registrerte filen (i dette tilfellet, /sw.js) ligger og dens underkataloger.

Service Worker-omfang

Hvis du vil begrense omfanget, kan du angi scope som det andre argumentet til register.

1navigator.serviceWorker.register('/sw.js', { scope: '/app/' })
2.then(registration => {
3    console.log(
4        'Service Worker registered with scope:',
5        registration.scope
6    );
7});

Forklaring

  • Med denne innstillingen vil bare sider under /app/ bli kontrollert av Service Worker.

Opprette Service Worker-filen

Deretter lager du en fil kalt sw.js og implementerer de grunnleggende hendelsene.

1// sw.js
2const CACHE_NAME = 'my-cache-v1';
3const urlsToCache = [
4    '/',
5    '/index.html',
6    '/styles.css',
7    '/script.js',
8    '/offline.html'
9];

Denne koden definerer en liste over ressurser som skal bufres.

Roller og mekanismer for hver hendelse

install

 1// Install event (initial caching)
 2self.addEventListener('install', event => {
 3    console.log('[ServiceWorker] Install');
 4    event.waitUntil(
 5        caches.open(CACHE_NAME).then(cache => {
 6            console.log('[ServiceWorker] Caching app shell');
 7            return cache.addAll(urlsToCache);
 8        })
 9    );
10});
  • self.addEventListener('install') trigges når Service Worker-en registreres for første gang. På dette stadiet blir nødvendige filer forhåndslagret i cache.

activate

 1// Activation event (delete old caches)
 2self.addEventListener('activate', event => {
 3    console.log('[ServiceWorker] Activate');
 4    event.waitUntil(
 5        caches.keys().then(keyList => {
 6            return Promise.all(keyList.map(key => {
 7                if (key !== CACHE_NAME) {
 8                    console.log('[ServiceWorker] Removing old cache:', key);
 9                    return caches.delete(key);
10                }
11            }));
12        })
13    );
14    return self.clients.claim();
15});
  • I activate-hendelsen slettes gammel cache for å optimalisere lagringen. Kun cachen til den nye versjonen beholdes.

fetch

1// Fetch event (cache-first strategy)
2self.addEventListener('fetch', event => {
3  console.log('[ServiceWorker] Fetch', event.request.url);
4    event.respondWith(
5        caches.match(event.request).then(response => {
6            return response || fetch(event.request).catch(() => caches.match('/offline.html'));
7        })
8    );
9});

Alle HTTP-forespørsler fanges opp—hvis en cachet versjon finnes, returneres den; ellers hentes den fra nettverket. Når du er offline, returneres en alternativ side (f.eks. offline.html).

Bekrefter operasjonen

La oss faktisk sjekke hvordan Service Worker fungerer.

 1document.getElementById('fetchBtn').addEventListener('click', () => {
 2    fetch('/style.css')
 3        .then(response => response.text())
 4        .then(data => {
 5            console.log('Fetched data:', data);
 6        })
 7        .catch(error => {
 8            console.error('Fetch failed:', error);
 9        });
10});
  • Her sjekker vi registreringen av Service Worker og oppførselen ved henting av ressurser ved å klikke på testknappen.

Eksempler på cache-strategier

Følgende er vanlige cache-strategier:.

Cache først

Her er et eksempel på implementering av Cache First-strategien:.

1self.addEventListener('fetch', event => {
2    event.respondWith(
3        caches.match(event.request).then(response => {
4            return response || fetch(event.request);
5        })
6    );
7});
  • Denne koden implementerer en cache-først-strategi, der den forespurte ressursen returneres fra hurtigbufferet hvis tilgjengelig; hvis ikke, hentes den fra nettverket.

Nettverk først

Her er et eksempel på implementering av Network First-strategien:.

 1self.addEventListener('fetch', event => {
 2    event.respondWith(
 3        fetch(event.request)
 4            .then(response => {
 5                return caches.open(CACHE_NAME).then(cache => {
 6                    cache.put(event.request, response.clone());
 7                    return response;
 8                });
 9            })
10            .catch(() => caches.match(event.request))
11    );
12});
  • Denne koden implementerer en nettverk-først-strategi, der den forespurte ressursen først hentes fra nettverket, og hvis det feiler, hentes den fra hurtigbufferet.

Cache kun stilark og JavaScript, men bruk API-er i sanntid

Her er et eksempel på implementering hvor stilark og JavaScript cache-es, mens API-er aksesseres i sanntid:.

 1self.addEventListener('fetch', event => {
 2    if (event.request.url.includes('/api/')) {
 3        // Fetch API responses in real-time without caching
 4        return;
 5    }
 6
 7    // Use cache-first strategy for static files
 8    event.respondWith(
 9        caches.match(event.request).then(response => {
10            return response || fetch(event.request);
11        })
12    );
13});
  • Denne koden får alltid tilgang til API-forespørsler i sanntid, og bruker en cache-først-strategi for statiske filer som stilark og JavaScript.

Oppdateringsflyt

Oppdateringsflyten for en Service Worker er som følger:.

  1. En ny sw.js oppdages.
  2. install-hendelsen trigges.
  3. Den venter til forrige Service Worker blir inaktiv.
  4. activate-hendelsen trigges.
  5. Den bytter til ny Service Worker.
  6. controllerchange-hendelsen utløses.

Oppdateringsdeteksjon

Når en Service Worker er installert, brukes den gamle fortsatt frem til neste besøk. For å ta i bruk oppdateringer er det vanlig å bruke kode som oppdager oppdateringer og laster inn siden på nytt.

1navigator.serviceWorker.addEventListener('controllerchange', () => {
2    window.location.reload();
3});
  • controllerchange-hendelsen utløses når kontrolløren for Service Workeren, altså Service Workeren som kontrollerer den nåværende siden, endres.
  • Sider som allerede er åpne fortsetter å bruke den nåværende Service Workeren, og den nylig installerte Service Workeren trer ikke i kraft på disse sidene umiddelbart. Derfor brukes en teknikk der controllerchange-hendelsen benyttes for å oppdage at en ny kontrollør har blitt aktiv, og deretter lastes siden inn på nytt for umiddelbart å ta i bruk oppdateringen.

Forsiktighetsregler og beste praksis

Når du bruker Service Worker, bør du huske på følgende punkter:.

  • HTTPS påkrevd På grunn av sikkerhetsbegrensninger fungerer det ikke over http:// med mindre det er på localhost.

  • Hash-baserte filnavn Cachenavnet kan inneholde filnavn, URL og versjonsinformasjon.

  • Kommunikasjon med klienter Bruk postMessage for å kommunisere mellom Service Worker og JavaScript på siden.

Sammendrag

Service Worker er en essensiell teknologi for offline-støtte og ytelsesforbedringer i nettapplikasjoner. Ved å forstå det grunnleggende forløpet med installasjon, aktivering og håndtering av forespørsler, samt implementere passende cache-strategier, kan du lage webapplikasjoner av høyere kvalitet.

Du kan følge med på artikkelen ovenfor ved å bruke Visual Studio Code på vår YouTube-kanal. Vennligst sjekk ut YouTube-kanalen.

YouTube Video