Data offline

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

Penyimpanan

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 nilai/kunci, menggunakan penyimpanan lokal atau penyimpanan sesi. Ini tidak tersedia dalam konteks pekerja layanan. API ini sinkron, sehingga tidak disarankan untuk penyimpanan data yang kompleks.
  • Penyimpanan Cache: Seperti dibahas dalam Modul cache.

Anda dapat mengelola semua penyimpanan perangkat dengan Storage Manager API pada 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 memainkan peran penting dalam membuat PWA berfungsi dengan andal ketika jaringan tidak stabil atau tidak ada. Namun, kapan Anda harus menggunakan keduanya?

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. Data ini mencakup data yang harus dapat ditelusuri atau digabungkan dengan cara seperti NoSQL, atau data lain seperti data spesifik per 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. Langkah ini akan membuat {i>database<i} baru jika belum ada. IndexedDB adalah API asinkron, tetapi memerlukan callback, bukan menampilkan Promise. Contoh berikut menggunakan library idb 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 merupakan sebuah opsi.

Contoh berikut membuat database untuk menyimpan resep memasak.

Membuat dan membuka database

Untuk membuka database:

  1. Gunakan fungsi openDB untuk membuat database IndexedDB baru yang bernama cookbook. Karena database IndexedDB memiliki versi, Anda perlu menambah nomor versi setiap kali membuat perubahan pada struktur database. Parameter kedua adalah versi database. Dalam contoh ini ditetapkan ke 1.
  2. Objek inisialisasi yang berisi callback upgrade() diteruskan ke openDB(). Fungsi callback dipanggil saat database diinstal pertama kali atau saat database diupgrade ke versi baru. Fungsi ini adalah satu-satunya tempat di mana 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 juga harus terjadi. Biasanya, fungsi upgrade() berisi pernyataan switch tanpa pernyataan break untuk memungkinkan setiap langkah dilakukan 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 toko dan membuat indeks lain bernama type, berdasarkan properti type.

Mari kita lihat penyimpanan objek yang baru saja dibuat. Setelah menambahkan resep ke penyimpanan objek dan membuka DevTools di browser berbasis Chromium atau Web Inspector di Safari, inilah yang akan Anda lihat:

Safari dan Chrome menampilkan konten IndexedDB.

Menambahkan data

IndexedDB menggunakan transaksi. Transaksi mengelompokkan tindakan bersama, sehingga terjadi sebagai satu unit. Keduanya membantu memastikan bahwa {i>database<i} selalu dalam keadaan konsisten. Hal ini juga sangat penting jika Anda memiliki beberapa salinan aplikasi yang berjalan, untuk mencegah penulisan yang bersamaan ke data yang sama. Untuk menambahkan data:

  1. Mulai transaksi dengan mode yang ditetapkan ke readwrite.
  2. Dapatkan penyimpanan objek, tempat Anda akan menambahkan data.
  3. Panggil add() dengan data yang Anda simpan. Metode ini menerima data dalam bentuk kamus (sebagai pasangan kunci/nilai) dan menambahkannya ke penyimpanan objek. Kamus harus dapat di-clone menggunakan Kloning Terstruktur. Jika ingin memperbarui objek yang sudah ada, Anda perlu memanggil metode put().

Transaksi memiliki promise done yang akan diselesaikan ketika transaksi berhasil diselesaikan, atau ditolak dengan error transaksi.

Seperti yang dijelaskan dalam dokumentasi library IDB, jika Anda menulis ke database, tx.done merupakan sinyal bahwa semuanya berhasil di-commit ke database. Namun, sebaiknya tunggu operasi individual 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 dalam database bersama resep lain. ID akan secara otomatis ditetapkan dan ditambahkan oleh indexedDB. Jika Anda menjalankan kode ini dua kali, Anda akan memiliki dua entri cookie yang sama.

Mengambil data

Berikut adalah cara mendapatkan data dari IndexedDB:

  1. Mulai transaksi dan tentukan toko atau penyimpanan objek, serta jenis transaksi (opsional).
  2. Panggil objectStore() dari transaksi tersebut. Pastikan Anda menentukan nama penyimpanan objek.
  3. Panggil get() dengan kunci yang ingin Anda dapatkan. Secara default, penyimpanan 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 digunakan bersama di antara semua opsi penyimpanan, termasuk Cache Storage, IndexedDB, Web Storage, dan bahkan file service worker dan dependensinya. Namun, jumlah penyimpanan yang tersedia bervariasi untuk setiap browser. Anda mungkin tidak akan kehabisan data; situs dapat menyimpan megabyte dan bahkan gigabyte data di beberapa browser. Misalnya, Chrome memungkinkan browser menggunakan hingga 80% total kapasitas disk, dan tiap 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, kuota, dan penggunaannya. Contoh berikut menggunakan Storage API untuk mendapatkan estimasi kuota dan penggunaan, lalu menghitung persentase yang digunakan dan byte yang tersisa. Perlu diperhatikan bahwa navigator.storage menampilkan instance StorageManager. Ada antarmuka Storage yang terpisah dan mudah membingungkan mereka.

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 diperinci berdasarkan penggunaan, 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 penyimpanan dan penggunaan untuk asal saat ini.

Persistensi data

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

Untuk meminta penyimpanan persisten, panggil StorageManager.persist(). Seperti sebelumnya, antarmuka StorageManager adalah akses 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 dalam asal saat ini dengan memanggil StorageManager.persisted(). Firefox meminta izin dari pengguna untuk menggunakan penyimpanan persisten. Browser berbasis Chromium memberikan atau menolak persistensi berdasarkan heuristik untuk menentukan pentingnya konten bagi pengguna. Salah satu kriteria untuk Google Chrome, 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

Dukungan Browser

  • 4
  • 12
  • 3,5
  • 4

Sumber

Akses Sistem File

Dukungan Browser

  • 86
  • 86
  • 111
  • 15.2

Sumber

Pengelola Penyimpanan

Dukungan Browser

  • 55
  • 79
  • 57
  • 15.2

Sumber

Referensi