`Service Worker` di JavaScript

`Service Worker` di JavaScript

Artikel ini menjelaskan konsep Service Worker di JavaScript.

Kami akan menjelaskan langkah demi langkah mulai dari dasar-dasar Service Worker hingga kontrol cache secara praktis.

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

Service Worker adalah fitur JavaScript yang berada di antara browser dan jaringan, memungkinkan caching permintaan dan dukungan offline. Ini adalah teknologi inti dari PWA (Progressive Web Apps) dan memberikan pengalaman seperti aplikasi native pada aplikasi web.

Apa itu Service Worker?

Service Worker adalah file JavaScript yang dijalankan di background thread pada browser. Ia berjalan pada thread terpisah dari halaman, tidak dapat mengakses UI, tetapi dapat mencegat permintaan jaringan, mengelola cache, dan menangani push notification.

Fitur utama dari Service Worker meliputi hal-hal berikut:.

  • Hanya bekerja melalui HTTPS, kecuali di localhost.
  • Menggunakan API asynchronous berbasis Promise.
  • Berbasis event, menggunakan event seperti install, activate, fetch, dan push.

Mendaftarkan Service Worker

Pertama, mari kita tulis kode untuk mendaftarkan Service Worker di browser.

 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 (file Service Worker).
  • Anda dapat menggunakan then untuk menangani keberhasilan dan catch untuk menangani kesalahan saat pendaftaran.
  • registration.scope mewakili rentang path (cakupan) yang dipengaruhi oleh Service Worker.
  • Secara default, cakupannya adalah direktori tempat file yang didaftarkan (dalam hal ini, /sw.js) berada dan subdirektorinya.

Cakupan Service Worker

Jika Anda ingin membatasi cakupannya, Anda dapat menentukan scope menggunakan argumen kedua dari 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 pengaturan ini, hanya halaman di bawah /app/ yang akan dikendalikan oleh Service Worker.

Membuat file Service Worker

Selanjutnya, buat file bernama sw.js dan implementasikan event-event dasar.

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

Kode ini mendefinisikan daftar sumber daya yang akan di-cache.

Peran dan mekanisme masing-masing event

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') dipicu ketika Service Worker didaftarkan untuk pertama kalinya. Pada tahap ini, file-file yang diperlukan akan di-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});
  • Pada event activate, cache lama akan dihapus untuk mengoptimalkan penyimpanan. Hanya cache dari versi terbaru yang disimpan.

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 dicegat—jika ada versi yang sudah di-cache, itu akan dikembalikan; jika tidak ada, diambil dari jaringan. Ketika offline, halaman alternatif (misal, offline.html) akan dikembalikan.

Mengonfirmasi operasi

Mari kita periksa bagaimana cara kerja Service Worker.

 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 memeriksa pendaftaran Service Worker dan perilaku pengambilan sumber daya dengan mengklik tombol uji.

Contoh Strategi Caching

Berikut adalah strategi caching yang umum:.

Cache First

Berikut contoh implementasi untuk strategi 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});
  • Kode ini menerapkan strategi cache-first, di mana sumber daya yang diminta akan dikembalikan dari cache jika tersedia; jika tidak, akan diambil dari jaringan.

Network First

Berikut contoh implementasi untuk strategi 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});
  • Kode ini menerapkan strategi network-first, di mana sumber daya yang diminta diambil terlebih dahulu dari jaringan, dan jika gagal, akan diambil dari cache.

Cache hanya file gaya dan JavaScript, akses API secara real-time

Berikut adalah contoh implementasi di mana file gaya dan JavaScript di-cache, sementara API diakses secara real-time:.

 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});
  • Kode ini selalu mengakses permintaan API secara real-time dan menerapkan strategi cache-first untuk file statis seperti stylesheet dan JavaScript.

Alur pembaruan

Alur pembaruan Service Worker adalah sebagai berikut:.

  1. sw.js baru terdeteksi.
  2. Event install dijalankan.
  3. Menunggu hingga Service Worker sebelumnya tidak aktif.
  4. Event activate dijalankan.
  5. Beralih ke Service Worker yang baru.
  6. Event controllerchange dijalankan.

Deteksi pembaruan

Setelah Service Worker terpasang, versi lama akan tetap digunakan sampai kunjungan berikutnya. Untuk menerapkan pembaruan, biasanya digunakan kode yang mendeteksi pembaruan dan memuat ulang halaman.

1navigator.serviceWorker.addEventListener('controllerchange', () => {
2    window.location.reload();
3});
  • Event controllerchange dijalankan ketika pengendali Service Worker, yaitu Service Worker yang mengendalikan halaman saat ini, berubah.
  • Halaman yang sudah terbuka akan tetap menggunakan Service Worker saat ini, dan Service Worker yang baru dipasang tidak langsung berlaku di halaman-halaman tersebut. Oleh karena itu, digunakan teknik di mana event controllerchange digunakan untuk mendeteksi bahwa pengendali baru telah aktif, kemudian halaman dimuat ulang agar pembaruan segera diterapkan.

Peringatan dan Praktik Terbaik

Saat menggunakan Service Worker, perhatikan hal-hal berikut:.

  • HTTPS Diperlukan Karena batasan keamanan, tidak akan bekerja pada http:// kecuali di localhost.

  • Nama File dengan Hash Nama cache dapat mencakup nama file, URL, dan informasi versi.

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

Ringkasan

Service Worker adalah teknologi penting untuk dukungan offline dan peningkatan performa pada aplikasi web. Dengan memahami alur dasar instalasi, aktivasi, dan penanganan fetch, serta menerapkan strategi caching yang tepat, Anda dapat membangun aplikasi web dengan kualitas lebih tinggi.

Anda dapat mengikuti artikel di atas menggunakan Visual Studio Code di saluran YouTube kami. Silakan periksa juga saluran YouTube kami.

YouTube Video