`Service Worker` dalam JavaScript

`Service Worker` dalam JavaScript

Artikel ini menerangkan konsep Service Worker dalam JavaScript.

Kami akan menerangkan secara langkah demi langkah dari asas Service Worker hingga kawalan cache secara praktikal.

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 dalam JavaScript

Service Worker ialah ciri dalam JavaScript yang berada di antara pelayar web dan rangkaian, membolehkan caching permintaan serta sokongan luar talian. Ia merupakan teknologi teras bagi PWA (Progressive Web Apps) dan memberikan pengalaman seperti aplikasi asli kepada aplikasi web.

Apakah itu Service Worker?

Service Worker ialah fail JavaScript yang dijalankan di thread latar belakang pelayar web. Ia berjalan di thread yang berasingan daripada halaman, tidak boleh mengakses UI, tetapi boleh memintas permintaan rangkaian, mengurus cache, dan mengendalikan notifikasi push.

Ciri utama Service Worker termasuk yang berikut:.

  • Ia hanya berfungsi melalui HTTPS, kecuali di localhost.
  • Ia menggunakan API asynchronous berasaskan Promise.
  • Ia dipacu oleh peristiwa (event-driven), menggunakan peristiwa seperti install, activate, fetch, dan push.

Mendaftar Service Worker

Pertama sekali, mari kita tulis kod untuk mendaftarkan Service Worker dalam pelayar web.

 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}

Penjelasan

  • Gunakan navigator.serviceWorker.register() untuk mendaftarkan /sw.js (fail Service Worker).
  • Anda boleh menggunakan then untuk pengendalian kejayaan dan catch untuk pengendalian ralat semasa pendaftaran.
  • registration.scope mewakili julat laluan (skop) yang terjejas oleh Service Worker.
  • Secara lalai, skop adalah direktori di mana fail yang didaftarkan (dalam kes ini, /sw.js) terletak dan subdirektorinya.

Skop Service Worker

Jika anda ingin mengehadkan skop, anda boleh menentukan scope menggunakan argumen kedua pada 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});

Penjelasan

  • Dengan tetapan ini, hanya halaman di bawah /app/ akan dikawal oleh Service Worker.

Mencipta fail Service Worker

Seterusnya, cipta fail bernama sw.js dan laksanakan peristiwa-peristiwa asas.

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];

Kod ini mentakrifkan senarai sumber yang akan disimpan dalam cache.

Peranan dan mekanisme bagi setiap peristiwa

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') dicetuskan apabila Service Worker didaftarkan buat kali pertama. Pada peringkat ini, fail-fail yang diperlukan akan dimasukkan ke dalam cache terlebih dahulu.

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});
  • Dalam peristiwa activate, cache lama akan dipadam untuk mengoptimumkan storan. Hanya cache versi baharu yang dikekalkan.

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});

Semua permintaan HTTP akan dipintas—jika versi yang telah dicache wujud, ia akan dipulangkan; jika tidak, ia diambil dari rangkaian. Apabila luar talian, halaman alternatif (contohnya, offline.html) akan dipulangkan.

Mengesahkan operasi

Mari kita lihat dengan lebih lanjut bagaimana Service Worker berfungsi.

 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});
  • Di sini, kita menyemak pendaftaran Service Worker dan tingkah laku mendapatkan sumber dengan menekan butang uji.

Contoh Strategi Caching

Berikut ialah strategi caching yang biasa digunakan:.

Cache Diutamakan (Cache First)

Berikut adalah contoh pelaksanaan untuk strategi Cache Diutamakan (Cache First):.

1self.addEventListener('fetch', event => {
2    event.respondWith(
3        caches.match(event.request).then(response => {
4            return response || fetch(event.request);
5        })
6    );
7});
  • Kod ini melaksanakan strategi cache-first, di mana sumber yang diminta akan dikembalikan dari cache jika tersedia; jika tidak, ia akan dimuat turun dari rangkaian.

Rangkaian Diutamakan (Network First)

Berikut adalah contoh pelaksanaan untuk strategi Rangkaian Diutamakan (Network First):.

 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});
  • Kod ini melaksanakan strategi network-first, di mana sumber yang diminta akan dimuat turun dari rangkaian terlebih dahulu, dan jika gagal, ia akan diambil dari cache.

Cache hanya gaya dan JavaScript, akses API secara masa nyata

Berikut adalah contoh pelaksanaan di mana gaya dan JavaScript dicache tetapi API diakses secara masa nyata:.

 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});
  • Kod ini sentiasa mengakses permintaan API secara masa nyata dan mengaplikasikan strategi cache-first pada fail statik seperti stylesheet dan JavaScript.

Aliran kemas kini

Aliran kemas kini untuk Service Worker adalah seperti berikut:.

  1. sw.js yang baharu dikesan.
  2. Peristiwa install dicetuskan.
  3. Ia menunggu sehingga Service Worker sebelum ini tidak lagi aktif.
  4. Peristiwa activate dicetuskan.
  5. Ia bertukar kepada Service Worker yang baharu.
  6. Acara controllerchange dicetuskan.

Pengesanan kemas kini

Setelah Service Worker dipasang, yang lama akan terus digunakan sehingga lawatan seterusnya. Untuk melaksanakan kemas kini, adalah biasa untuk menggunakan kod yang mengesan kemas kini dan memuat semula halaman.

1navigator.serviceWorker.addEventListener('controllerchange', () => {
2    window.location.reload();
3});
  • Acara controllerchange dicetuskan apabila pengawal Service Worker, iaitu Service Worker yang mengawal halaman semasa, berubah.
  • Halaman yang sudah dibuka akan kekal menggunakan Service Worker semasa, dan Service Worker yang baru dipasang tidak akan berkuat kuasa serta-merta pada halaman tersebut. Oleh itu, satu teknik digunakan di mana acara controllerchange digunakan untuk mengesan bahawa pengawal baharu telah aktif, kemudian halaman dimuat semula untuk melaksanakan kemas kini dengan segera.

Langkah Berhati-hati dan Amalan Terbaik

Apabila menggunakan Service Worker, sila ambil perhatian perkara berikut:.

  • HTTPS Diperlukan Disebabkan sekatan keselamatan, ia tidak berfungsi melalui http:// kecuali pada localhost.

  • Nama Fail Bertanda Hash Nama cache boleh mengandungi nama fail, URL, dan maklumat versi.

  • Komunikasi dengan Klien Gunakan postMessage untuk berkomunikasi antara Service Worker dan JavaScript halaman.

Ringkasan

Service Worker ialah teknologi penting untuk sokongan luar talian dan penambahbaikan prestasi dalam aplikasi web. Dengan memahami aliran asas pemasangan, pengaktifan dan pengendalian fetch, serta melaksanakan strategi caching yang sesuai, anda boleh membina aplikasi web yang lebih berkualiti tinggi.

Anda boleh mengikuti artikel di atas menggunakan Visual Studio Code di saluran YouTube kami. Sila lihat juga saluran YouTube kami.

YouTube Video