Cara membuat aplikasi web Anda bekerja secara offline

Kekuatan JavaScript dan API browser

Dunia menjadi lebih saling terhubung - jumlah orang dengan akses ke Internet telah tumbuh menjadi 4,5 miliar .

gambar

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.

Perangkat Lunak EDISON - pengembangan web
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.

gambar

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 kami

Sepanjang 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:

gambar

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 .

gambar

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


gambar

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.

gambar

Nomor demo 2


gambar

Demo kami sekarang terlihat sama saat boot, terlepas dari status jaringan kami!

Bagus Ayo sekarang coba gunakan aplikasi kita.

gambar

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


gambar

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 !?

gambar

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); // QUEUE IT UP return { ...state, list, }; 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); addToDB(action); // QUEUE IT UP } } return { ...state, list, }; 

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); // sync with the server } }; }; }); 

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.

gambar

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

Source: https://habr.com/ru/post/id474374/


All Articles