Data offline

Untuk membuat pengalaman offline yang solid, PWA Anda memerlukan pengelolaan penyimpanan. Di bab penyimpanan cache, Anda telah mempelajari bahwa penyimpanan cache adalah salah satu opsi untuk menyimpan data di perangkat. Dalam bab ini, kami akan menunjukkan cara mengelola data offline, termasuk persistensi data, batas, dan alat yang tersedia.

Penyimpanan tidak hanya mencakup file dan aset, tetapi dapat mencakup jenis data lainnya. Di semua browser yang mendukung PWA, API berikut tersedia untuk penyimpanan di perangkat:

  • IndexedDB: Opsi penyimpanan objek NoSQL untuk data terstruktur dan blob (data biner).
  • WebStorage: Cara menyimpan pasangan string kunci/nilai, menggunakan penyimpanan lokal atau penyimpanan sesi. Tidak tersedia dalam konteks pekerja layanan. API ini bersifat sinkron sehingga tidak direkomendasikan untuk penyimpanan data yang kompleks.
  • Penyimpanan Cache: Seperti yang telah dibahas dalam modul Penyimpanan dalam cache.

Anda dapat mengelola semua penyimpanan perangkat dengan Storage Manager API di platform yang didukung. Cache Storage API dan IndexedDB memberikan akses asinkron ke penyimpanan persisten untuk PWA dan dapat diakses dari thread utama, pekerja web, dan pekerja layanan. Keduanya berperan penting dalam membuat PWA berfungsi dengan andal saat jaringan tidak stabil atau tidak ada. Namun, kapan Anda harus menggunakan masing-masing?

Gunakan Cache Storage API untuk resource jaringan, hal-hal yang akan Anda akses dengan memintanya melalui URL, seperti HTML, CSS, JavaScript, gambar, video, dan audio.

Gunakan IndexedDB untuk menyimpan data terstruktur. Hal ini mencakup data yang perlu ditelusuri atau digabungkan dengan cara seperti NoSQL, atau data lain seperti data khusus pengguna yang tidak selalu cocok dengan permintaan URL. Perhatikan bahwa IndexedDB tidak dirancang untuk penelusuran teks lengkap.

IndexedDB

Untuk menggunakan IndexedDB, buka database terlebih dahulu. Tindakan ini akan membuat database baru jika belum ada. IndexedDB adalah API asinkron, tetapi memerlukan callback, bukan menampilkan Promise. Contoh berikut menggunakan library idb dari Jake Archibald, yang merupakan wrapper Promise kecil untuk IndexedDB. Library helper tidak diperlukan untuk menggunakan IndexedDB, tetapi jika Anda ingin menggunakan sintaksis Promise, library idb adalah opsi yang tersedia.

Contoh berikut membuat database untuk menyimpan resep masakan.

Membuat dan membuka database

Untuk membuka database:

  1. Gunakan fungsi openDB untuk membuat database IndexedDB baru yang disebut cookbook. Karena database IndexedDB memiliki versi, Anda perlu meningkatkan nomor versi setiap kali membuat perubahan pada struktur database. Parameter kedua adalah versi database. Dalam contoh, ditetapkan ke 1.
  2. Objek inisialisasi yang berisi callback upgrade() diteruskan ke openDB(). Fungsi callback dipanggil saat database diinstal untuk pertama kalinya atau saat diupgrade ke versi baru. Fungsi ini adalah satu-satunya tempat tindakan dapat terjadi. Tindakan dapat mencakup pembuatan penyimpanan objek baru (struktur yang digunakan IndexedDB untuk mengatur data), atau indeks (yang ingin Anda telusuri). Di sinilah migrasi data harus terjadi. Biasanya, fungsi upgrade() berisi pernyataan switch tanpa pernyataan break untuk memungkinkan setiap langkah terjadi secara berurutan, berdasarkan versi lama database.
import { openDB } from 'idb';

async function createDB() {
  // Using https://github.com/jakearchibald/idb
  const db = await openDB('cookbook', 1, {
    upgrade(db, oldVersion, newVersion, transaction) {
      // Switch over the oldVersion, *without breaks*, to allow the database to be incrementally upgraded.
    switch(oldVersion) {
     case 0:
       // Placeholder to execute when database is created (oldVersion is 0)
     case 1:
       // Create a store of objects
       const store = db.createObjectStore('recipes', {
         // The `id` property of the object will be the key, and be incremented automatically
           autoIncrement: true,
           keyPath: 'id'
       });
       // Create an index called `name` based on the `type` property of objects in the store
       store.createIndex('type', 'type');
     }
   }
  });
}

Contoh ini membuat penyimpanan objek di dalam database cookbook yang disebut recipes, dengan properti id ditetapkan sebagai kunci indeks penyimpanan dan membuat indeks lain yang disebut type, berdasarkan properti type.

Mari kita lihat object store yang baru saja dibuat. Setelah menambahkan resep ke object store dan membuka DevTools di browser berbasis Chromium atau Web Inspector di Safari, Anda akan melihat hal berikut:

Safari dan Chrome menampilkan konten IndexedDB.

Menambahkan data

IndexedDB menggunakan transaksi. Transaksi mengelompokkan tindakan bersama-sama, sehingga tindakan tersebut terjadi sebagai satu unit. Fungsi ini membantu memastikan database selalu dalam status yang konsisten. Ini juga penting, jika Anda memiliki beberapa salinan aplikasi yang berjalan, untuk mencegah penulisan simultan ke data yang sama. Untuk menambahkan data:

  1. Mulai transaksi dengan mode ditetapkan ke readwrite.
  2. Dapatkan object store, tempat Anda akan menambahkan data.
  3. Panggil add() dengan data yang Anda simpan. Metode ini menerima data dalam bentuk kamus (sebagai key-value pair) dan menambahkannya ke penyimpanan objek. Kamus harus dapat di-clone menggunakan Pembuatan Clone Terstruktur. Jika ingin memperbarui objek yang ada, Anda harus memanggil metode put().

Transaksi memiliki promise done yang di-resolve saat transaksi berhasil diselesaikan, atau ditolak dengan error transaksi.

Seperti yang dijelaskan dalam dokumentasi library IDB, jika Anda menulis ke database, tx.done adalah sinyal bahwa semuanya berhasil di-commit ke database. Namun, sebaiknya tunggu setiap operasi agar Anda dapat melihat error yang menyebabkan transaksi gagal.

// Using https://github.com/jakearchibald/idb
async function addData() {
  const cookies = {
      name: "Chocolate chips cookies",
      type: "dessert",
        cook_time_minutes: 25
  };
  const tx = await db.transaction('recipes', 'readwrite');
  const store = tx.objectStore('recipes');
  store.add(cookies);
  await tx.done;
}

Setelah Anda menambahkan cookie, resep akan berada di database bersama resep lainnya. ID ditetapkan dan ditambah secara otomatis oleh indexedDB. Jika menjalankan kode ini dua kali, Anda akan memiliki dua entri cookie yang identik.

Mengambil data

Berikut adalah cara mendapatkan data dari IndexedDB:

  1. Mulai transaksi dan tentukan satu atau beberapa object store, dan secara opsional jenis transaksi.
  2. Panggil objectStore() dari transaksi tersebut. Pastikan Anda menentukan nama penyimpanan objek.
  3. Panggil get() dengan kunci yang ingin Anda dapatkan. Secara default, toko menggunakan kuncinya sebagai indeks.
// Using https://github.com/jakearchibald/idb
async function getData() {
  const tx = await db.transaction('recipes', 'readonly')
  const store = tx.objectStore('recipes');
// Because in our case the `id` is the key, we would
// have to know in advance the value of the id to
// retrieve the record
  const value = await store.get([id]);
}

Pengelola penyimpanan

Mengetahui cara mengelola penyimpanan PWA sangat penting untuk menyimpan dan melakukan streaming respons jaringan dengan benar.

Kapasitas penyimpanan dibagikan di antara semua opsi penyimpanan, termasuk Penyimpanan Cache, IndexedDB, Penyimpanan Web, dan bahkan file pekerja layanan serta dependensinya. Namun, jumlah penyimpanan yang tersedia bervariasi di setiap browser. Anda tidak akan kehabisan ruang penyimpanan; situs dapat menyimpan data berukuran megabyte dan bahkan gigabyte di beberapa browser. Misalnya, Chrome memungkinkan browser menggunakan hingga 80% dari total kapasitas disk, dan setiap origin dapat menggunakan hingga 60% dari seluruh kapasitas disk. Untuk browser yang mendukung Storage API, Anda dapat mengetahui jumlah penyimpanan yang masih tersedia untuk aplikasi, kuotanya, dan penggunaannya. Contoh berikut menggunakan Storage API untuk mendapatkan estimasi kuota dan penggunaan, lalu menghitung persentase yang digunakan dan byte yang tersisa. Perhatikan bahwa navigator.storage menampilkan instance StorageManager. Ada antarmuka Storage terpisah dan mudah untuk membingungkan keduanya.

if (navigator.storage && navigator.storage.estimate) {
  const quota = await navigator.storage.estimate();
  // quota.usage -> Number of bytes used.
  // quota.quota -> Maximum number of bytes available.
  const percentageUsed = (quota.usage / quota.quota) * 100;
  console.log(`You've used ${percentageUsed}% of the available storage.`);
  const remaining = quota.quota - quota.usage;
  console.log(`You can write up to ${remaining} more bytes.`);
}

Di Chromium DevTools, Anda dapat melihat kuota situs dan jumlah penyimpanan yang digunakan yang dikelompokkan berdasarkan apa yang menggunakannya, dengan membuka bagian Penyimpanan di tab Aplikasi.

Chrome DevTools di Aplikasi, bagian Hapus Penyimpanan

Firefox dan Safari tidak menawarkan layar ringkasan untuk melihat semua kuota dan penggunaan penyimpanan untuk origin saat ini.

Persistensi data

Anda dapat meminta penyimpanan persisten ke browser di platform yang kompatibel untuk menghindari penghapusan data otomatis setelah tidak aktif atau pada tekanan penyimpanan. Jika diizinkan, browser tidak akan pernah menghapus data dari penyimpanan. Perlindungan ini mencakup pendaftaran pekerja layanan, database IndexedDB, dan file dalam penyimpanan cache. Perhatikan bahwa pengguna selalu memegang kendali, dan mereka dapat menghapus penyimpanan kapan saja, meskipun browser telah memberikan penyimpanan persisten.

Untuk meminta penyimpanan persisten, panggil StorageManager.persist(). Seperti sebelumnya, antarmuka StorageManager diakses melalui properti navigator.storage.

async function persistData() {
  if (navigator.storage && navigator.storage.persist) {
    const result = await navigator.storage.persist();
    console.log(`Data persisted: ${result}`);
}

Anda juga dapat memeriksa apakah penyimpanan persisten sudah diberikan di origin saat ini dengan memanggil StorageManager.persisted(). Firefox meminta izin dari pengguna untuk menggunakan penyimpanan persisten. Browser berbasis Chromium memberikan atau menolak persistensi berdasarkan heuristika untuk menentukan tingkat kepentingan konten bagi pengguna. Salah satu kriteria untuk Google Chrome adalah, misalnya, penginstalan PWA. Jika pengguna telah menginstal ikon untuk PWA di sistem operasi, browser dapat memberikan penyimpanan persisten.

Mozilla Firefox meminta izin persistensi penyimpanan kepada pengguna.

Dukungan Browser API

Penyimpanan Web

Browser Support

  • Chrome: 4.
  • Edge: 12.
  • Firefox: 3.5.
  • Safari: 4.

Source

Akses Sistem File

Browser Support

  • Chrome: 86.
  • Edge: 86.
  • Firefox: 111.
  • Safari: 15.2.

Source

Pengelola Penyimpanan

Browser Support

  • Chrome: 55.
  • Edge: 79.
  • Firefox: 57.
  • Safari: 15.2.

Source

Resource