Membuat halaman penggantian offline

Apa kesamaan yang dimiliki aplikasi Asisten Google, aplikasi Slack, Zoom, dan hampir semua aplikasi khusus platform lainnya di ponsel atau komputer? Benar, fitur ini setidaknya selalu memberi Anda sesuatu. Meskipun tidak memiliki koneksi jaringan, Anda masih dapat membuka aplikasi Asisten, atau 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 tidak memiliki koneksi jaringan, Anda tidak akan pernah mendapatkan apa pun.

Sebaliknya, di Web, Anda biasanya tidak mendapatkan apa pun saat offline. Chrome menghadirkan game dino offline, tapi itu saja.

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

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

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

Halaman penggantian offline dengan pekerja layanan kustom

Namun, tidak harus demikian. Berkat pekerja layanan dan Cache Storage API, Anda dapat memberikan pengalaman offline yang disesuaikan untuk pengguna. Halaman ini dapat berupa halaman bermerek sederhana dengan informasi bahwa pengguna saat ini sedang offline, tetapi juga dapat 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. Biasanya, Anda melakukannya setelah aplikasi dimuat.

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

Kode pekerja layanan

Isi file pekerja layanan yang sebenarnya mungkin tampak sedikit rumit pada awalnya, tetapi komentar dalam contoh di bawah akan menjelaskan semuanya. Ide utamanya adalah melakukan pra-cache file bernama offline.html yang hanya ditayangkan pada permintaan navigasi yang gagal, dan membiarkan 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 dapat membuat materi iklan dan menyesuaikannya dengan kebutuhan serta menambahkan merek Anda. Contoh di bawah menunjukkan hal minimum yang dapat dilakukan. 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 tentang membuat aplikasi dapat diinstal

Setelah situs Anda memiliki halaman penggantian offline, Anda mungkin bertanya-tanya tentang langkah berikutnya. Agar aplikasi dapat diinstal, Anda perlu menambahkan manifes aplikasi web dan secara opsional membuat strategi penginstalan.

Catatan tambahan tentang cara menayangkan halaman penggantian offline dengan Workbox.js

Anda mungkin pernah mendengar Workbox. Workbox adalah sekumpulan library JavaScript untuk menambahkan dukungan offline ke aplikasi web. Jika Anda lebih memilih untuk menulis lebih sedikit kode pekerja layanan sendiri, Anda dapat menggunakan urutan langkah Workbox untuk hanya halaman offline.

Selanjutnya, pelajari cara menentukan strategi penginstalan untuk aplikasi Anda.