Kekuatan JavaScript dan API browserDunia menjadi lebih saling terhubung - jumlah orang dengan akses ke Internet telah tumbuh menjadi
4,5 miliar .
Tetapi data ini tidak mencerminkan jumlah orang yang memiliki koneksi Internet yang lambat atau rusak. Bahkan di Amerika Serikat,
4,9 juta rumah tidak dapat mengakses akses Internet kabel dengan kecepatan lebih dari 3 megabit per detik.
Seluruh dunia - mereka yang memiliki akses Internet andal - masih rentan kehilangan konektivitas.
Beberapa faktor yang dapat mempengaruhi kualitas koneksi jaringan Anda meliputi:
- Cakupan buruk dari penyedia.
- Kondisi cuaca ekstrem.
- Pemadaman listrik.
- Pengguna yang jatuh ke zona mati, seperti gedung yang memblokir koneksi jaringan mereka.
- Perjalanan kereta api dan perjalanan terowongan.
- Koneksi yang dikendalikan oleh pihak ketiga dan terbatas waktu.
- Praktik budaya yang memerlukan akses Internet terbatas atau tidak ada pada waktu atau hari tertentu.
Dengan ini, jelas bahwa kita harus mempertimbangkan pengalaman otonom ketika mengembangkan dan membuat aplikasi.

Artikel ini diterjemahkan dengan dukungan EDISON Software, sebuah perusahaan yang melakukan pesanan luar biasa dari Cina Selatan , dan juga mengembangkan aplikasi dan situs web .
Saya baru-baru ini memiliki kesempatan untuk menambahkan otonomi ke aplikasi yang ada menggunakan pekerja layanan, penyimpanan cache dan IndexedDB. Pekerjaan teknis yang diperlukan untuk aplikasi untuk bekerja offline dikurangi menjadi empat tugas terpisah, yang akan saya bahas dalam posting ini.
Pekerja layanan
Aplikasi yang dibuat untuk penggunaan offline tidak harus sangat tergantung jaringan. Secara konseptual, ini hanya mungkin jika, jika terjadi kegagalan, opsi cadangan ada.
Jika aplikasi web gagal dimuat, kita harus mengambil sumber daya untuk browser di suatu tempat (HTML / CSS / JavaScript). Dari mana sumber daya ini berasal, jika bukan dari permintaan jaringan? Bagaimana dengan cache. Kebanyakan orang akan setuju bahwa lebih baik menyediakan antarmuka pengguna yang mungkin sudah ketinggalan zaman daripada halaman kosong.
Peramban terus-menerus menanyakan data. Layanan caching data sebagai fallback masih mengharuskan kami untuk mencegat permintaan browser dan menulis aturan caching. Di sinilah pekerja layanan berperan - anggap mereka sebagai perantara.

Pekerja layanan hanyalah file JavaScript tempat kami dapat berlangganan acara dan menulis aturan kami sendiri untuk caching dan menangani kegagalan jaringan.
Mari kita mulai.
Harap dicatat: aplikasi demo kamiSepanjang posting ini, kami akan menambahkan fungsi yang berdiri sendiri ke aplikasi demo. Aplikasi demo adalah halaman sederhana untuk mengambil / menyewa buku di perpustakaan. Kemajuan akan disajikan sebagai serangkaian GIF, dan penggunaan simulasi Chrome DevTools offline.
Ini adalah kondisi awal:

Tugas 1 - Caching Sumber Daya Statis
Sumber daya statis adalah sumber daya yang tidak sering berubah. HTML, CSS, JavaScript, dan gambar mungkin termasuk dalam kategori ini. Browser mencoba memuat sumber daya statis menggunakan permintaan yang dapat dicegat oleh pekerja layanan.
Mari kita mulai dengan mendaftarkan pekerja layanan kami.
if ('serviceWorker' in navigator) { window.addEventListener('load', function() { navigator.serviceWorker.register('/sw.js'); }); }
Pekerja layanan adalah pekerja
web di bawah tenda dan karenanya harus diimpor dari file JavaScript terpisah. Registrasi dilakukan menggunakan metode
register
setelah memuat situs.
Sekarang kita memiliki pekerja layanan yang dimuat, mari kita cache sumber daya statis kita.
var CACHE_NAME = 'my-offline-cache'; var urlsToCache = [ '/', '/static/css/main.c9699bb9.css', '/static/js/main.99348925.js' ]; self.addEventListener('install', function(event) { event.waitUntil( caches.open(CACHE_NAME) .then(function(cache) { return cache.addAll(urlsToCache); }) ); });
Karena kami mengontrol URL sumber daya statis, kami dapat menyimpannya segera setelah inisialisasi pekerja layanan menggunakan
Cache Storage
.

Sekarang cache kami penuh dengan sumber daya statis yang paling baru diminta, mari kita muat sumber daya ini dari cache jika terjadi kegagalan permintaan.
self.addEventListener('fetch', function(event) { event.respondWith( fetch(event.request).catch(function() { caches.match(event.request).then(function(response) { return response; } ); ); });
Acara
fetch
dipecat setiap kali browser membuat permintaan. Handler event
fetch
baru kami sekarang memiliki logika tambahan untuk mengembalikan respons yang di-cache jika terjadi pemadaman jaringan.
Demo nomor 1

Aplikasi demo kami sekarang dapat menyajikan sumber daya statis secara offline! Tapi di mana data kami?
Tugas 2 - Caching Sumber Daya Dinamis
Aplikasi satu halaman (SPA) biasanya meminta data secara bertahap setelah pemuatan awal halaman, dan aplikasi demo kami tidak terkecuali - daftar buku tidak segera dimuat. Data ini biasanya berasal dari permintaan XHR yang mengembalikan respons yang sering berubah untuk memberikan status baru untuk aplikasi - sehingga bersifat dinamis.
Caching sumber daya dinamis sebenarnya sangat mirip dengan caching sumber daya statis - perbedaan utamanya adalah kita perlu memperbarui cache lebih sering. Membuat daftar lengkap dari semua permintaan dinamis XHR yang mungkin juga cukup sulit, jadi kami akan menyimpannya saat mereka tiba, dan tidak memiliki daftar yang telah ditentukan, seperti yang kami lakukan untuk sumber daya statis.
Lihatlah penangan
fetch
kami:
self.addEventListener('fetch', function(event) { event.respondWith( fetch(event.request).catch(function() { caches.match(event.request).then(function(response) { return response; } ); ); });
Kami dapat menyesuaikan implementasi ini dengan menambahkan beberapa kode yang menyembunyikan permintaan dan respons yang berhasil. Ini memastikan bahwa kami terus-menerus menambahkan permintaan baru ke cache kami dan terus-menerus memperbarui data yang di-cache.
self.addEventListener('fetch', function(event) { event.respondWith( fetch(event.request) .then(function(response) { caches.open(CACHE_NAME).then(function(cache) { cache.put(event.request, response); }); }) .catch(function() { caches.match(event.request).then(function(response) { return response; } ); ); });
Cache Storage
kami saat ini memiliki beberapa entri.

Nomor demo 2

Demo kami sekarang terlihat sama saat boot, terlepas dari status jaringan kami!
Bagus Ayo sekarang coba gunakan aplikasi kita.

Sayangnya, pesan kesalahan ada di mana-mana. Tampaknya semua interaksi kami dengan antarmuka tidak berfungsi. Saya tidak bisa memilih atau menyerahkan buku itu! Apa yang perlu diperbaiki?
Tugas 3 - Membangun Antarmuka Pengguna yang Optimis
Saat ini, masalah dengan aplikasi kami adalah bahwa logika pengumpulan data kami masih sangat tergantung pada respons jaringan. Tindakan check-in atau check-out mengirimkan permintaan ke server dan mengharapkan respons yang berhasil. Ini bagus untuk konsistensi data, tetapi buruk untuk pengalaman mandiri kami.
Agar interaksi ini bekerja offline, kita perlu membuat aplikasi kita lebih
optimis . Interaksi yang optimistis tidak memerlukan respons dari server dan bersedia menampilkan tampilan data yang diperbarui. Operasi optimis yang biasa di sebagian besar aplikasi web adalah
delete
- mengapa tidak memberikan umpan balik instan kepada pengguna jika kami sudah memiliki semua informasi yang diperlukan?
Memutuskan sambungan aplikasi kami dari jaringan menggunakan pendekatan optimis relatif mudah diterapkan.
case CHECK_OUT_SUCCESS: case CHECK_OUT_FAILURE: list = [...state.list]; list.push(action.payload); return { ...state, list, }; case CHECK_IN_SUCCESS: case CHECK_IN_FAILURE; list = [...state.list]; for (let i = 0; i < list.length; i++) { if (list[i].id === action.payload.id) { list.splice(i, 1, action.payload); } } return { ...state, list, };
Kuncinya adalah menangani tindakan pengguna dengan cara yang sama - terlepas dari apakah permintaan jaringan berhasil atau tidak. Cuplikan kode di atas diambil dari redux reducer aplikasi kita,
SUCCESS
dan
FAILURE
diluncurkan tergantung pada ketersediaan jaringan. Terlepas dari bagaimana permintaan jaringan selesai, kami akan memperbarui daftar buku kami.
Nomor demo 3

Interaksi pengguna sekarang terjadi secara online (tidak secara harfiah). Tombol "check-in" dan "check-out" memperbarui antarmuka sesuai, meskipun pesan merah konsol menunjukkan bahwa permintaan jaringan tidak dieksekusi.
Bagus! Hanya ada satu masalah kecil dengan perenderan offline yang optimis ...
Bukankah kita kehilangan kembalian kita !?

Tugas 4 - Antri tindakan pengguna untuk sinkronisasi
Kami perlu melacak tindakan yang dilakukan oleh pengguna saat dia offline, sehingga kami dapat menyinkronkannya dengan server kami ketika pengguna kembali ke jaringan. Ada beberapa mekanisme penyimpanan di browser yang dapat bertindak sebagai antrian tindakan, dan kami akan menggunakan IndexedDB. IndexedDB menyediakan beberapa hal yang tidak akan Anda dapatkan dari LocalStorage:
- Operasi non-pemblokiran asinkron
- Batas penyimpanan yang jauh lebih tinggi
- Manajemen transaksi
Lihatlah kode peredam lama kami:
case CHECK_OUT_SUCCESS: case CHECK_OUT_FAILURE: list = [...state.list]; list.push(action.payload); return { ...state, list, }; case CHECK_IN_SUCCESS: case CHECK_IN_FAILURE; list = [...state.list]; for (let i = 0; i < list.length; i++) { if (list[i].id === action.payload.id) { list.splice(i, 1, action.payload); } } return { ...state, list, };
Mari memodifikasinya untuk menyimpan acara check-in dan check-out di IndexedDB selama acara
FAILURE
.
case CHECK_OUT_FAILURE: list = [...state.list]; list.push(action.payload); addToDB(action);
Berikut adalah implementasi membuat IndexedDB bersama dengan
addToDB
addToDB.
let db = indexedDB.open('actions', 1); db.onupgradeneeded = function(event) { let db = event.target.result; db.createObjectStore('requests', { autoIncrement: true }); }; const addToDB = action => { var db = indexedDB.open('actions', 1); db.onsuccess = function(event) { var db = event.target.result; var objStore = db .transaction(['requests'], 'readwrite') .objectStore('requests'); objStore.add(action); }; };
Sekarang semua tindakan pengguna offline kami disimpan dalam memori browser, kami dapat menggunakan pendengar acara browser
online
untuk menyinkronkan data saat koneksi dipulihkan.
window.addEventListener('online', () => { const db = indexedDB.open('actions', 1); db.onsuccess = function(event) { let db = event.target.result; let objStore = db .transaction(['requests'], 'readwrite') .objectStore('requests'); objStore.getAll().onsuccess = function(event) { let requests = event.target.result; for (let request of requests) { send(request);
Pada tahap ini, kita dapat menghapus antrian dari semua permintaan yang berhasil kita kirim ke server.
Nomor demo 4
Demo terakhir terlihat sedikit lebih rumit. Di sebelah kanan, di jendela terminal gelap, semua aktivitas API dicatat. Demo ini melibatkan offline, memilih beberapa buku dan kembali online.

Jelas bahwa permintaan yang dibuat offline diantrikan dan dikirim sekaligus ketika pengguna kembali online.
Pendekatan "bermain" ini agak naif - misalnya, kita mungkin tidak perlu membuat dua permintaan jika kita menerima dan mengembalikan buku yang sama. Ini juga tidak akan berfungsi jika beberapa orang menggunakan aplikasi yang sama.
Itu saja
Keluar dan buat aplikasi web Anda offline! Posting ini menunjukkan beberapa dari banyak hal yang dapat Anda lakukan untuk menambahkan fitur mandiri ke aplikasi Anda, dan jelas tidak lengkap.
Untuk mempelajari lebih lanjut, lihat
Dasar-Dasar Web Google . Untuk melihat implementasi offline lainnya, lihat
pembicaraan ini .

Baca juga blognya
Perusahaan EDISON:
20 perpustakaan untuk
aplikasi iOS yang spektakuler