Membuat halaman penggantian offline

Apa kesamaan antara aplikasi Asisten Google, aplikasi Slack, aplikasi Zoom, dan hampir semua aplikasi khusus platform lainnya di ponsel atau komputer Anda? Benar, setidaknya selalu memberi Anda sesuatu. Meskipun tidak memiliki koneksi jaringan, Anda masih dapat membuka aplikasi Asisten, masuk ke Slack, atau meluncurkan Zoom. Anda mungkin tidak mendapatkan sesuatu yang sangat berarti atau bahkan tidak dapat mencapai apa yang ingin dicapai, tetapi setidaknya Anda mendapatkan sesuatu dan aplikasi berada dalam kontrol.

Aplikasi seluler Asisten Google saat offline.
Asisten Google.

Aplikasi seluler Slack saat offline.
Slack.

Zoom aplikasi seluler saat offline.
Zoom.

Dengan aplikasi khusus platform, meskipun Anda tidak memiliki koneksi jaringan, Anda tidak akan pernah mendapatkan apa pun.

Sebaliknya, di Web, biasanya Anda tidak mendapatkan apa pun saat offline. Chrome memberi Anda game dino offline, dan ini saja.

Aplikasi seluler Google Chrome menampilkan game dino offline.
Google Chrome untuk iOS.

Aplikasi desktop Google Chrome menampilkan game dino offline.
Google Chrome untuk macOS.

Di Web, saat Anda tidak memiliki koneksi jaringan, Anda secara default tidak akan mendapatkan apa pun.

Halaman penggantian offline dengan pekerja layanan kustom

Namun, sebenarnya tidak harus seperti ini. Berkat pekerja layanan dan Cache Storage API, Anda dapat memberikan pengalaman offline yang disesuaikan bagi pengguna. Halaman ini dapat berupa halaman bermerek sederhana dengan informasi bahwa pengguna sedang offline, tetapi juga bisa menjadi solusi yang lebih kreatif, seperti, misalnya, game labirin offline trivago yang terkenal dengan tombol Hubungkan kembali manual dan hitung mundur upaya koneksi ulang otomatis.

Halaman offline trivago dengan labirin offline trivago.
Labirin offline trivago.

Mendaftarkan pekerja layanan

Cara untuk melakukannya adalah melalui pekerja layanan. Anda dapat mendaftarkan pekerja layanan dari halaman utama seperti dalam contoh kode di bawah ini. Biasanya Anda melakukannya setelah aplikasi dimuat.

window.addEventListener("load", () => {
  if ("serviceWorker" in navigator) {
    navigator.serviceWorker.register("service-worker.js");
  }
});

Kode pekerja layanan

Konten file pekerja layanan yang sebenarnya mungkin tampak sedikit rumit pada awalnya, tetapi komentar dalam contoh di bawah ini akan menjelaskan semuanya. Ide utamanya adalah melakukan pra-cache file bernama offline.html yang hanya ditayangkan pada permintaan navigasi yang gagal, dan memungkinkan browser menangani semua kasus lainnya:

/*
Copyright 2015, 2019, 2020, 2021 Google LLC. All Rights Reserved.
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 http://www.apache.org/licenses/LICENSE-2.0
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

// Incrementing OFFLINE_VERSION will kick off the install event and force
// previously cached resources to be updated from the network.
// This variable is intentionally declared and unused.
// Add a comment for your linter if you want:
// eslint-disable-next-line no-unused-vars
const OFFLINE_VERSION = 1;
const CACHE_NAME = "offline";
// Customize this with a different URL if needed.
const OFFLINE_URL = "offline.html";

self.addEventListener("install", (event) => {
  event.waitUntil(
    (async () => {
      const cache = await caches.open(CACHE_NAME);
      // Setting {cache: 'reload'} in the new request ensures that the
      // response isn't fulfilled from the HTTP cache; i.e., it will be
      // from the network.
      await cache.add(new Request(OFFLINE_URL, { cache: "reload" }));
    })()
  );
  // Force the waiting service worker to become the active service worker.
  self.skipWaiting();
});

self.addEventListener("activate", (event) => {
  event.waitUntil(
    (async () => {
      // Enable navigation preload if it's supported.
      // See https://developers.google.com/web/updates/2017/02/navigation-preload
      if ("navigationPreload" in self.registration) {
        await self.registration.navigationPreload.enable();
      }
    })()
  );

  // Tell the active service worker to take control of the page immediately.
  self.clients.claim();
});

self.addEventListener("fetch", (event) => {
  // Only call event.respondWith() if this is a navigation request
  // for an HTML page.
  if (event.request.mode === "navigate") {
    event.respondWith(
      (async () => {
        try {
          // First, try to use the navigation preload response if it's
          // supported.
          const preloadResponse = await event.preloadResponse;
          if (preloadResponse) {
            return preloadResponse;
          }

          // Always try the network first.
          const networkResponse = await fetch(event.request);
          return networkResponse;
        } catch (error) {
          // catch is only triggered if an exception is thrown, which is
          // likely due to a network error.
          // If fetch() returns a valid HTTP response with a response code in
          // the 4xx or 5xx range, the catch() will NOT be called.
          console.log("Fetch failed; returning offline page instead.", error);

          const cache = await caches.open(CACHE_NAME);
          const cachedResponse = await cache.match(OFFLINE_URL);
          return cachedResponse;
        }
      })()
    );
  }

  // If our if() condition is false, then this fetch handler won't
  // intercept the request. If there are any other fetch handlers
  // registered, they will get a chance to call event.respondWith().
  // If no fetch handlers call event.respondWith(), the request
  // will be handled by the browser as if there were no service
  // worker involvement.
});

Halaman penggantian offline

File offline.html adalah tempat Anda bisa berkreasi dan menyesuaikannya dengan kebutuhan Anda serta menambahkan branding. Contoh di bawah menunjukkan hal-hal minimum yang memungkinkan. Contoh ini mendemonstrasikan pemuatan ulang manual berdasarkan penekanan tombol serta pemuatan ulang otomatis berdasarkan peristiwa online dan polling server reguler.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <title>You are offline</title>

    <!-- Inline the page's stylesheet. -->
    <style>
      body {
        font-family: helvetica, arial, sans-serif;
        margin: 2em;
      }

      h1 {
        font-style: italic;
        color: #373fff;
      }

      p {
        margin-block: 1rem;
      }

      button {
        display: block;
      }
    </style>
  </head>
  <body>
    <h1>You are offline</h1>

    <p>Click the button below to try reloading.</p>
    <button type="button">⤾ Reload</button>

    <!-- Inline the page's JavaScript file. -->
    <script>
      // Manual reload feature.
      document.querySelector("button").addEventListener("click", () => {
        window.location.reload();
      });

      // Listen to changes in the network state, reload when online.
      // This handles the case when the device is completely offline.
      window.addEventListener('online', () => {
        window.location.reload();
      });

      // Check if the server is responding and reload the page if it is.
      // This handles the case when the device is online, but the server
      // is offline or misbehaving.
      async function checkNetworkAndReload() {
        try {
          const response = await fetch('.');
          // Verify we get a valid response from the server
          if (response.status >= 200 && response.status < 500) {
            window.location.reload();
            return;
          }
        } catch {
          // Unable to connect to the server, ignore.
        }
        window.setTimeout(checkNetworkAndReload, 2500);
      }

      checkNetworkAndReload();
    </script>
  </body>
</html>

Demo

Anda dapat melihat cara kerja halaman penggantian offline di demo yang disematkan di bawah. Jika tertarik, Anda dapat mempelajari kode sumber di Glitch.

Catatan tambahan untuk membuat aplikasi Anda dapat diinstal

Sekarang setelah situs Anda memiliki halaman penggantian offline, Anda mungkin bertanya-tanya tentang langkah selanjutnya. Agar aplikasi dapat diinstal, Anda perlu menambahkan manifes aplikasi web dan, jika perlu, membuat strategi penginstalan.

Catatan tambahan tentang menayangkan halaman penggantian offline dengan Workbox.js

Anda mungkin pernah mendengar tentang Workbox. Workbox adalah kumpulan library JavaScript untuk menambahkan dukungan offline ke aplikasi web. Jika lebih suka menulis sendiri kode pekerja layanan yang lebih sedikit, Anda dapat menggunakan urutan langkah Workbox untuk halaman offline saja.

Selanjutnya, pelajari cara menentukan strategi penginstalan untuk aplikasi Anda.