Praktik Terbaik dalam Menggunakan IndexedDB

Pelajari praktik terbaik untuk menyinkronkan status aplikasi antara IndexedDB sebuah library pengelolaan status yang populer.

Saat pengguna pertama kali memuat situs atau aplikasi, sering kali ada cukup banyak pekerjaan yang perlu dilakukan dalam membangun status aplikasi awal yang digunakan untuk merender UI. Misalnya, terkadang aplikasi perlu mengautentikasi sisi klien pengguna, lalu membuat beberapa permintaan API sebelum memiliki semua data yang diperlukan untuk ditampilkan di halaman.

Menyimpan status aplikasi di IndexedDB dapat menjadi cara yang tepat untuk mempercepat waktu pemuatan untuk kunjungan berulang. Selanjutnya, aplikasi dapat melakukan sinkronisasi dengan layanan API apa pun di latar belakang dan mengupdate UI dengan data baru dengan lambat, menggunakan strategi stale-Temporary- revalidate.

Penggunaan lain yang tepat bagi IndexedDB adalah untuk menyimpan konten buatan pengguna, baik sebagai penyimpanan sementara sebelum diupload ke server atau sebagai cache sisi klien untuk data jarak jauh - atau, tentu saja, keduanya.

Namun, saat menggunakan IndexedDB, ada banyak hal penting yang perlu dipertimbangkan yang mungkin tidak terlalu jelas bagi developer yang baru mengenal API. Artikel ini menjawab pertanyaan umum dan membahas beberapa hal terpenting yang perlu diingat saat menyimpan data di IndexedDB.

Menjaga aplikasi Anda tetap dapat diprediksi

Banyak kompleksitas seputar IndexedDB berasal dari fakta bahwa ada begitu banyak faktor yang tidak dapat Anda kendalikan (pengembang). Bagian ini mendalami banyak masalah yang harus Anda ingat saat bekerja dengan IndexedDB.

Tidak semuanya dapat disimpan di IndexedDB di semua platform

Jika Anda menyimpan file besar buatan pengguna, seperti gambar atau video, Anda dapat mencoba menyimpannya sebagai objek File atau Blob. Cara ini akan berfungsi di beberapa platform, tetapi gagal di platform lainnya. Safari di iOS, khususnya, tidak dapat menyimpan Blob di IndexedDB.

Untungnya tidak terlalu sulit untuk mengonversi Blob menjadi ArrayBuffer, dan sebaliknya. Menyimpan ArrayBuffer di IndexedDB sangat didukung.

Namun, perlu diingat bahwa Blob memiliki jenis MIME sedangkan ArrayBuffer tidak. Anda harus menyimpan jenis tersebut di samping buffer untuk melakukan konversi dengan benar.

Untuk mengonversi ArrayBuffer menjadi Blob, Anda cukup menggunakan konstruktor Blob.

function arrayBufferToBlob(buffer, type) {
  return new Blob([buffer], { type: type });
}

Arah lainnya sedikit lebih kompleks, dan merupakan proses asinkron. Anda dapat menggunakan objek FileReader untuk membaca blob sebagai ArrayBuffer. Setelah pembacaan selesai, peristiwa loadend akan dipicu pada pembaca. Anda dapat menggabungkan proses ini dalam Promise seperti berikut:

function blobToArrayBuffer(blob) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.addEventListener('loadend', () => {
      resolve(reader.result);
    });
    reader.addEventListener('error', reject);
    reader.readAsArrayBuffer(blob);
  });
}

Menulis ke penyimpanan mungkin gagal

Error saat menulis ke IndexedDB dapat terjadi karena berbagai alasan, dan dalam beberapa kasus, alasan ini berada di luar kendali Anda sebagai developer. Misalnya, beberapa browser saat ini tidak mengizinkan penulisan ke IndexedDB saat dalam mode penjelajahan rahasia. Ada juga kemungkinan bahwa pengguna menggunakan perangkat yang kapasitas disknya hampir habis, dan browser akan membatasi Anda agar tidak menyimpan apa pun.

Oleh karena itu, Anda harus selalu menerapkan penanganan error yang benar dalam kode IndexedDB. Ini juga berarti sebaiknya menjaga status aplikasi di memori (selain menyimpannya), sehingga UI tidak rusak saat berjalan dalam mode penjelajahan rahasia atau jika ruang penyimpanan tidak tersedia (meskipun beberapa fitur aplikasi lain yang memerlukan penyimpanan tidak akan berfungsi).

Anda dapat menangkap error dalam operasi IndexedDB dengan menambahkan pengendali peristiwa untuk peristiwa error setiap kali Anda membuat objek IDBDatabase, IDBTransaction, atau IDBRequest.

const request = db.open('example-db', 1);
request.addEventListener('error', (event) => {
  console.log('Request error:', request.error);
};

Data yang disimpan mungkin telah diubah atau dihapus oleh pengguna

Tidak seperti database sisi server yang memungkinkan Anda membatasi akses tidak sah, database sisi klien dapat diakses oleh ekstensi browser dan alat developer, serta dapat dihapus oleh pengguna.

Meskipun mungkin tidak umum bagi pengguna untuk mengubah data mereka yang disimpan secara lokal, pengguna biasanya perlu menghapusnya. Aplikasi Anda harus dapat menangani kedua kasus ini tanpa error.

Data yang disimpan mungkin sudah tidak berlaku

Serupa dengan bagian sebelumnya, meskipun pengguna belum mengubah data itu sendiri, data yang mereka miliki dalam penyimpanan juga mungkin ditulis oleh versi lama kode Anda, mungkin versi dengan bug di dalamnya.

IndexedDB memiliki dukungan bawaan untuk versi skema dan melakukan upgrade melalui metode IDBOpenDBRequest.onupgradeneeded(); namun, Anda masih harus menulis kode upgrade sedemikian rupa agar dapat menangani pengguna yang berasal dari versi sebelumnya (termasuk versi yang memiliki bug).

Pengujian unit dapat sangat membantu di sini, karena sering kali sulit untuk menguji secara manual semua kemungkinan jalur dan kasus upgrade.

Menjaga performa aplikasi Anda

Salah satu fitur utama IndexedDB adalah API asinkronnya, namun jangan biarkan hal tersebut mengecoh Anda sehingga berpikir bahwa Anda tidak perlu khawatir dengan kinerja saat menggunakannya. Ada sejumlah kasus saat penggunaan yang tidak tepat masih dapat memblokir thread utama, yang dapat menyebabkan jank dan respons tidak responsif.

Sebagai aturan umum, pembacaan dan penulisan ke IndexedDB tidak boleh lebih besar dari yang diperlukan untuk data yang diakses.

Meskipun IndexedDB memungkinkan untuk menyimpan objek besar yang disusun bertingkat sebagai catatan tunggal (dan hal ini memang cukup nyaman dari perspektif developer), praktik ini harus dihindari. Alasannya adalah karena saat IndexedDB menyimpan objek, IndexedDB harus membuat clone terstruktur terlebih dahulu dari objek tersebut, dan proses cloning terstruktur akan terjadi di thread utama. Makin besar objek, makin lama waktu pemblokirannya.

Hal ini menimbulkan beberapa tantangan saat merencanakan cara mempertahankan status aplikasi ke IndexedDB, karena sebagian besar library pengelolaan status populer (seperti Redux) bekerja dengan mengelola seluruh hierarki status sebagai objek JavaScript tunggal.

Meskipun mengelola status dengan cara ini memiliki banyak manfaat (misalnya, membuat kode Anda mudah dipertimbangkan dan di-debug), dan meskipun hanya menyimpan seluruh hierarki status sebagai satu data di IndexedDB, mungkin terlihat menggoda dan nyaman, melakukan hal ini setelah setiap perubahan (meskipun di-throttle/debounce) akan mengakibatkan pemblokiran yang tidak perlu pada thread utama, hal ini akan meningkatkan kemungkinan error tulis, dan dalam beberapa kasus akan menyebabkan tab tidak responsif.

Daripada menyimpan seluruh hierarki status dalam satu kumpulan data, Anda sebaiknya memecahnya menjadi kumpulan data individual dan hanya memperbarui data yang benar-benar berubah.

Hal yang sama akan berlaku jika Anda menyimpan item besar seperti gambar, musik, atau video di IndexedDB. Simpan setiap item dengan kuncinya sendiri, bukan di dalam objek yang lebih besar, sehingga Anda dapat mengambil data terstruktur tanpa perlu membayar biaya pengambilan file biner.

Seperti kebanyakan praktik terbaik, aturan ini tidak berlaku sama sekali. Jika tidak mungkin untuk memecah objek status dan hanya menulis kumpulan perubahan minimal, memecah data menjadi sub-pohon dan hanya menulisnya masih lebih baik untuk selalu menulis seluruh hierarki status. Sedikit peningkatan lebih baik daripada tidak melakukan peningkatan sama sekali.

Terakhir, Anda harus selalu mengukur dampak performa dari kode yang Anda tulis. Meskipun memang benar bahwa penulisan kecil ke IndexedDB akan berperforma lebih baik daripada penulisan besar, hal ini hanya penting jika penulisan ke IndexedDB yang dilakukan aplikasi Anda benar-benar menyebabkan tugas panjang yang memblokir thread utama dan menurunkan pengalaman pengguna. Hal ini penting untuk melakukan pengukuran agar Anda memahami apa yang Anda optimalkan.

Kesimpulan

Developer dapat memanfaatkan mekanisme penyimpanan klien seperti IndexedDB untuk meningkatkan pengalaman pengguna aplikasi mereka dengan tidak hanya mempertahankan status di seluruh sesi, tetapi juga dengan mengurangi waktu yang diperlukan untuk memuat status awal pada kunjungan berulang.

Meskipun menggunakan IndexedDB dengan benar dapat meningkatkan pengalaman pengguna secara drastis, penggunaannya yang tidak benar atau gagal menangani kasus error dapat menyebabkan aplikasi yang rusak dan pengguna yang tidak puas.

Karena penyimpanan klien melibatkan banyak faktor di luar kendali Anda, kode Anda harus teruji dengan baik dan menangani error dengan benar, bahkan yang awalnya mungkin tidak mungkin terjadi.