Panduan Lengkap Untuk Aplikasi Web Progresif

Diterbitkan: 2022-03-10
Ringkasan cepat Dalam artikel ini, kita akan melihat poin-poin menyakitkan dari pengguna yang menjelajahi situs web non-PWA lama dan janji PWA untuk membuat web menjadi hebat. Anda akan mempelajari sebagian besar teknologi penting yang membuat PWA keren, seperti pekerja layanan, pemberitahuan push web, dan IndexedDB.

Itu adalah hari ulang tahun ayahku, dan aku ingin memesan kue coklat dan kemeja untuknya. Saya menuju ke Google untuk mencari kue coklat dan mengklik tautan pertama di hasil pencarian. Ada layar kosong selama beberapa detik; Saya tidak mengerti apa yang terjadi. Setelah beberapa detik menatap dengan sabar, layar ponsel saya dipenuhi dengan kue-kue yang tampak lezat. Segera setelah saya mengklik salah satu dari mereka untuk memeriksa detailnya, saya mendapat popup gemuk jelek, meminta saya untuk menginstal aplikasi Android sehingga saya bisa mendapatkan pengalaman halus seperti sutra saat memesan kue.

Itu mengecewakan. Hati nurani saya tidak mengizinkan saya untuk mengklik tombol "Instal". Yang ingin saya lakukan hanyalah memesan kue kecil dan segera berangkat.

Saya mengklik ikon salib di bagian paling kanan popup untuk keluar darinya sesegera mungkin. Tapi kemudian sembulan instalasi berada di bagian bawah layar, menempati seperempat ruang. Dan dengan UI yang tidak stabil, menggulir ke bawah adalah sebuah tantangan. Saya entah bagaimana berhasil memesan kue Belanda.

Setelah pengalaman mengerikan ini, tantangan saya berikutnya adalah memesan baju untuk ayah saya. Seperti sebelumnya, saya mencari kemeja di Google. Saya mengklik tautan pertama, dan dalam sekejap, seluruh konten berada tepat di depan saya. Scrollingnya lancar. Tidak ada spanduk pemasangan. Saya merasa seperti sedang menjelajahi aplikasi asli. Ada saat ketika koneksi internet saya yang buruk putus, tetapi saya masih dapat melihat kontennya alih-alih permainan dinosaurus. Bahkan dengan internet janky saya, saya berhasil memesan kemeja dan celana jeans untuk ayah saya. Yang paling mengejutkan, saya mendapatkan notifikasi tentang pesanan saya.

Saya akan menyebut ini pengalaman yang halus seperti sutra. Orang-orang ini melakukan sesuatu yang benar. Setiap situs web harus melakukannya untuk penggunanya. Ini disebut aplikasi web progresif.

Seperti yang dinyatakan Alex Russell di salah satu posting blognya:

“Terjadi di web dari waktu ke waktu bahwa teknologi yang kuat muncul tanpa manfaat dari departemen pemasaran atau pengemasan yang apik. Mereka berlama-lama dan tumbuh di pinggiran, menjadi topi tua bagi kelompok kecil sambil tetap hampir tidak terlihat oleh orang lain. Sampai seseorang menamainya.”

Pengalaman Halus Halus Di Web, Terkadang Dikenal Sebagai Aplikasi Web Progresif

Aplikasi web progresif (PWA) lebih merupakan metodologi yang melibatkan kombinasi teknologi untuk membuat aplikasi web yang kuat. Dengan pengalaman pengguna yang ditingkatkan, orang akan menghabiskan lebih banyak waktu di situs web dan melihat lebih banyak iklan. Mereka cenderung membeli lebih banyak, dan dengan pembaruan pemberitahuan, mereka cenderung sering berkunjung. Financial Times meninggalkan aplikasi aslinya pada tahun 2011 dan membangun aplikasi web menggunakan teknologi terbaik yang tersedia saat itu. Sekarang, produk tersebut telah berkembang menjadi PWA yang lengkap.

Tetapi mengapa, setelah sekian lama, Anda membuat aplikasi web ketika aplikasi asli melakukan tugasnya dengan cukup baik?

Mari kita lihat beberapa metrik yang dibagikan di Google IO 17.

Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

Lima miliar perangkat terhubung ke web, menjadikan web sebagai platform terbesar dalam sejarah komputasi. Di web seluler, 11,4 juta pengunjung unik bulanan mengunjungi 1000 properti web teratas, dan 4 juta mengunjungi seribu aplikasi teratas. Web seluler mengumpulkan sekitar empat kali lebih banyak pengguna daripada aplikasi asli. Tetapi jumlah ini turun tajam dalam hal pertunangan.

Seorang pengguna menghabiskan rata-rata 188,6 menit di aplikasi asli dan hanya 9,3 menit di web seluler. Aplikasi asli memanfaatkan kekuatan sistem operasi untuk mengirim pemberitahuan push guna memberikan pembaruan penting kepada pengguna. Mereka memberikan pengalaman pengguna yang lebih baik dan boot lebih cepat daripada situs web di browser. Alih-alih mengetik URL di browser web, pengguna hanya perlu mengetuk ikon aplikasi di layar beranda.

Sebagian besar pengunjung di web kemungkinan tidak akan kembali, jadi pengembang menemukan solusi untuk menunjukkan kepada mereka spanduk untuk memasang aplikasi asli, dalam upaya untuk membuat mereka tetap terlibat. Tetapi kemudian, pengguna harus melalui prosedur yang melelahkan dalam menginstal biner dari aplikasi asli. Memaksa pengguna untuk menginstal aplikasi sangat mengganggu dan semakin mengurangi kemungkinan mereka akan menginstalnya terlebih dahulu. Peluang untuk web jelas.

Bacaan yang direkomendasikan : Asli Dan PWA: Pilihan, Bukan Penantang!

Jika aplikasi web hadir dengan pengalaman pengguna yang kaya, pemberitahuan push, dukungan offline, dan pemuatan instan, mereka dapat menaklukkan dunia. Inilah yang dilakukan aplikasi web progresif.

PWA memberikan pengalaman pengguna yang kaya karena memiliki beberapa kekuatan:

  • Cepat
    UI tidak terkelupas. Scrolling lancar. Dan aplikasi merespons interaksi pengguna dengan cepat.

  • Dapat diandalkan
    Situs web normal memaksa pengguna untuk menunggu, tidak melakukan apa-apa, saat sedang sibuk melakukan perjalanan ke server. Sementara itu, PWA memuat data secara instan dari cache. PWA bekerja dengan mulus, bahkan pada koneksi 2G. Setiap permintaan jaringan untuk mengambil aset atau bagian data melewati pekerja layanan (lebih lanjut tentang itu nanti), yang pertama-tama memverifikasi apakah respons untuk permintaan tertentu sudah ada di cache. Ketika pengguna mendapatkan konten nyata hampir seketika, bahkan pada koneksi yang buruk, mereka lebih mempercayai aplikasi dan melihatnya sebagai lebih dapat diandalkan.

  • menarik
    PWA dapat memperoleh tempat di layar beranda pengguna. Ini menawarkan pengalaman seperti aplikasi asli dengan menyediakan area kerja layar penuh. Itu menggunakan pemberitahuan push untuk membuat pengguna tetap terlibat.

Sekarang setelah kita mengetahui apa yang dibawa PWA, mari masuk ke detail tentang apa yang memberi PWA keunggulan dibandingkan aplikasi asli. PWA dibangun dengan teknologi seperti service worker, manifes aplikasi web, pemberitahuan push, dan IndexedDB/struktur data lokal untuk caching. Mari kita lihat masing-masing secara detail.

Pekerja Layanan

Pekerja layanan adalah file JavaScript yang berjalan di latar belakang tanpa mengganggu interaksi pengguna. Semua permintaan GET ke server melalui service worker. Ini bertindak seperti proxy sisi klien. Dengan mencegat permintaan jaringan, dibutuhkan kontrol penuh atas respons yang dikirim kembali ke klien. PWA dimuat secara instan karena pekerja layanan menghilangkan ketergantungan pada jaringan dengan merespons dengan data dari cache.

Pekerja layanan hanya dapat mencegat permintaan jaringan yang ada dalam cakupannya. Misalnya, service worker dengan cakupan root dapat mencegat semua permintaan pengambilan yang berasal dari halaman web. Service worker beroperasi sebagai sistem yang digerakkan oleh peristiwa. Itu masuk ke keadaan tidak aktif ketika tidak diperlukan, sehingga menghemat memori. Untuk menggunakan service worker di aplikasi web, pertama-tama kita harus mendaftarkannya di halaman dengan JavaScript.

 (function main () { /* navigator is a WEB API that allows scripts to register themselves and carry out their activities. */ if ('serviceWorker' in navigator) { console.log('Service Worker is supported in your browser') /* register method takes in the path of service worker file and returns a promises, which returns the registration object */ navigator.serviceWorker.register('./service-worker.js').then (registration => { console.log('Service Worker is registered!') }) } else { console.log('Service Worker is not supported in your browser') } })()

Kami pertama-tama memeriksa apakah browser mendukung pekerja layanan. Untuk mendaftarkan service worker di aplikasi web, kami menyediakan URL-nya sebagai parameter untuk fungsi register , tersedia di navigator.serviceWorker ( navigator adalah API web yang memungkinkan skrip mendaftarkan diri dan menjalankan aktivitasnya). Seorang pekerja layanan terdaftar hanya sekali. Pendaftaran tidak terjadi pada setiap pemuatan halaman. Browser mengunduh file service worker ( ./service-worker.js ) hanya jika ada perbedaan byte antara service worker yang sudah diaktifkan dan yang lebih baru atau jika URL-nya telah berubah.

Pekerja layanan di atas akan mencegat semua permintaan yang datang dari root ( / ). Untuk membatasi cakupan pekerja layanan, kami akan meneruskan parameter opsional dengan salah satu kunci sebagai cakupannya.

 if ('serviceWorker' in navigator) { /* register method takes in an optional second parameter as an object. To restrict the scope of a service worker, the scope should be provided. scope: '/books' will intercept requests with '/books' in the url. */ navigator.serviceWorker.register('./service-worker.js', { scope: '/books' }).then(registration => { console.log('Service Worker for scope /books is registered', registration) }) }

Pekerja layanan di atas akan mencegat permintaan yang memiliki /books di URL. Misalnya, itu tidak akan mencegat permintaan dengan /products , tetapi bisa juga mencegat permintaan dengan /books/products .

Seperti disebutkan, pekerja layanan beroperasi sebagai sistem yang digerakkan oleh peristiwa. Itu mendengarkan acara (instal, aktifkan, ambil, dorong) dan karenanya memanggil pengendali acara masing-masing. Beberapa peristiwa ini adalah bagian dari siklus hidup pekerja layanan, yang melewati peristiwa ini secara berurutan untuk diaktifkan.

Instalasi

Setelah service worker berhasil didaftarkan, peristiwa penginstalan akan dipicu. Ini adalah tempat yang baik untuk melakukan pekerjaan inisialisasi, seperti menyiapkan cache atau membuat penyimpanan objek di IndexedDB. (IndexedDB akan lebih masuk akal bagi Anda setelah kami membahas detailnya. Untuk saat ini, kami dapat mengatakan bahwa ini adalah struktur pasangan nilai kunci.)

 self.addEventListener('install', (event) => { let CACHE_NAME = 'xyz-cache' let urlsToCache = [ '/', '/styles/main.css', '/scripts/bundle.js' ] event.waitUntil( /* open method available on caches, takes in the name of cache as the first parameter. It returns a promise that resolves to the instance of cache All the URLS above can be added to cache using the addAll method. */ caches.open(CACHE_NAME) .then (cache => cache.addAll(urlsToCache)) ) })

Di sini, kami melakukan caching beberapa file sehingga pemuatan berikutnya dapat dilakukan secara instan. self mengacu pada contoh pekerja layanan. event.waitUntil membuat service worker menunggu sampai semua kode di dalamnya selesai dieksekusi.

Pengaktifan

Setelah service worker dipasang, service worker belum dapat mendengarkan permintaan pengambilan. Sebaliknya, acara activate dipecat. Jika tidak ada pekerja layanan aktif yang beroperasi di situs web dalam cakupan yang sama, maka pekerja layanan yang diinstal akan segera diaktifkan. Namun, jika situs web sudah memiliki service worker yang aktif, maka aktivasi service worker baru akan ditunda hingga semua tab yang beroperasi pada service worker lama ditutup. Ini masuk akal karena pekerja layanan lama mungkin menggunakan instance cache yang sekarang dimodifikasi di yang lebih baru. Jadi, langkah aktivasi adalah tempat yang baik untuk menyingkirkan cache lama.

 self.addEventListener('activate', (event) => { let cacheWhitelist = ['products-v2'] // products-v2 is the name of the new cache event.waitUntil( caches.keys().then (cacheNames => { return Promise.all( cacheNames.map( cacheName => { /* Deleting all the caches except the ones that are in cacheWhitelist array */ if (cacheWhitelist.indexOf(cacheName) === -1) { return caches.delete(cacheName) } }) ) }) ) })

Pada kode di atas, kami menghapus cache lama. Jika nama cache tidak cocok dengan cacheWhitelist , maka akan dihapus. Untuk melewati fase menunggu dan segera mengaktifkan service worker, kami menggunakan skip.waiting() .

 self.addEventListener('activate', (event) => { self.skipWaiting() // The usual stuff })

Setelah pekerja layanan diaktifkan, ia dapat mendengarkan permintaan pengambilan dan peristiwa push.

Ambil Penangan Acara

Setiap kali halaman web menjalankan permintaan pengambilan untuk sumber daya melalui jaringan, peristiwa pengambilan dari pekerja layanan akan dipanggil. Ambil event handler terlebih dahulu mencari sumber daya yang diminta dalam cache. Jika ada dalam cache, maka ia mengembalikan respons dengan sumber daya yang di-cache. Jika tidak, ia memulai permintaan pengambilan ke server, dan ketika server mengirim kembali respons dengan sumber daya yang diminta, ia memasukkannya ke cache untuk permintaan berikutnya.

 /* Fetch event handler for responding to GET requests with the cached assets */ self.addEventListener('fetch', (event) => { event.respondWith( caches.open('products-v2') .then (cache => { /* Checking if the request is already present in the cache. If it is present, sending it directly to the client */ return cache.match(event.request).then (response => { if (response) { console.log('Cache hit! Fetching response from cache', event.request.url) return response } /* If the request is not present in the cache, we fetch it from the server and then put it in cache for subsequent requests. */ fetch(event.request).then (response => { cache.put(event.request, response.clone()) return response }) }) }) ) })

event.respondWith memungkinkan pekerja layanan mengirim respons yang disesuaikan ke klien.

Offline-pertama sekarang menjadi sesuatu. Untuk permintaan non-kritis apa pun, kami harus melayani respons dari cache, alih-alih melakukan perjalanan ke server. Jika aset apa pun tidak ada dalam cache, kami mendapatkannya dari server dan kemudian menyimpannya untuk permintaan berikutnya.

Pekerja layanan hanya bekerja di situs web HTTPS karena mereka memiliki kekuatan untuk memanipulasi respons permintaan pengambilan apa pun. Seseorang dengan niat jahat mungkin merusak respons untuk permintaan di situs web HTTP. Jadi, hosting PWA di HTTPS adalah wajib. Pekerja layanan tidak mengganggu fungsi normal DOM. Mereka tidak dapat berkomunikasi langsung dengan halaman web. Untuk mengirim pesan apa pun ke halaman web, itu menggunakan pesan pos.

Pemberitahuan Dorong Web

Misalkan Anda sedang sibuk bermain game di ponsel Anda, dan muncul notifikasi yang memberi tahu Anda tentang diskon 30% untuk merek favorit Anda. Tanpa basa-basi lagi, Anda mengklik notifikasi dan berbelanja napas Anda. Mendapatkan pembaruan langsung, katakanlah, pertandingan kriket atau sepak bola atau mendapatkan email dan pengingat penting karena pemberitahuan adalah masalah besar dalam hal melibatkan pengguna dengan suatu produk. Fitur ini hanya tersedia di aplikasi asli sampai PWA datang. PWA menggunakan pemberitahuan push web untuk bersaing dengan fitur canggih yang disediakan oleh aplikasi asli di luar kotak. Seorang pengguna akan tetap menerima pemberitahuan push web meskipun PWA tidak terbuka di tab browser mana pun dan bahkan jika browser tidak terbuka.

Aplikasi web harus meminta izin pengguna untuk mengirimi mereka pemberitahuan push.

Browser Prompt untuk meminta izin untuk pemberitahuan Web Push
Browser Prompt untuk meminta izin untuk pemberitahuan Web Push. (Pratinjau besar)

Setelah pengguna mengonfirmasi dengan mengklik tombol "Izinkan", token langganan unik dibuat oleh browser. Token ini unik untuk perangkat ini. Format token langganan yang dihasilkan oleh Chrome adalah sebagai berikut:

 { "endpoint": "https://fcm.googleapis.com/fcm/send/c7Veb8VpyM0:APA91bGnMFx8GIxf__UVy6vJ-n9i728CUJSR1UHBPAKOCE_SrwgyP2N8jL4MBXf8NxIqW6NCCBg01u8c5fcY0kIZvxpDjSBA75sVz64OocQ-DisAWoW7PpTge3SwvQAx5zl_45aAXuvS", "expirationTime": null, "keys": { "p256dh": "BJsj63kz8RPZe8Lv1uu-6VSzT12RjxtWyWCzfa18RZ0-8sc5j80pmSF1YXAj0HnnrkyIimRgLo8ohhkzNA7lX4w", "auth": "TJXqKozSJxcWvtQasEUZpQ" } }

Titik endpoint yang terkandung dalam token di atas akan unik untuk setiap langganan. Di situs web rata-rata, ribuan pengguna akan setuju untuk menerima pemberitahuan push, dan untuk masing-masing pengguna, endpoint ini akan unik. Jadi, dengan bantuan endpoint ini, aplikasi dapat menargetkan pengguna ini di masa mendatang dengan mengirimkan pemberitahuan push kepada mereka. expirationTime adalah jumlah waktu langganan berlaku untuk perangkat tertentu. Jika waktu expirationTime adalah 20 hari, itu berarti langganan push pengguna akan kedaluwarsa setelah 20 hari dan pengguna tidak akan dapat menerima notifikasi push pada langganan yang lebih lama. Dalam hal ini, browser akan membuat token langganan baru untuk perangkat tersebut. Kunci auth dan p256dh digunakan untuk enkripsi.

Sekarang, untuk mengirim pemberitahuan push ke ribuan pengguna ini di masa mendatang, pertama-tama kita harus menyimpan token langganan masing-masing. Adalah tugas server aplikasi (server back-end, mungkin skrip Node.js) untuk mengirim pemberitahuan push ke pengguna ini. Ini mungkin terdengar sesederhana membuat permintaan POST ke URL titik akhir dengan data notifikasi di payload permintaan. Namun, perlu dicatat bahwa jika pengguna tidak online saat pemberitahuan push yang ditujukan untuk mereka dipicu oleh server, mereka masih harus mendapatkan pemberitahuan itu setelah mereka kembali online. Server harus menangani skenario seperti itu, bersama dengan mengirim ribuan permintaan ke pengguna. Server yang melacak koneksi pengguna terdengar rumit. Jadi, sesuatu di tengah akan bertanggung jawab untuk merutekan pemberitahuan push web dari server ke klien. Ini disebut layanan push, dan setiap browser memiliki implementasi layanan pushnya sendiri. Browser harus memberi tahu informasi berikut ke layanan push untuk mengirim pemberitahuan apa pun:

  1. Waktu untuk hidup
    Ini adalah berapa lama pesan harus diantrekan, jika tidak dikirim ke pengguna. Setelah waktu ini berlalu, pesan akan dihapus dari antrian.
  2. Urgensi pesan
    Ini agar layanan push menghemat baterai pengguna dengan hanya mengirim pesan berprioritas tinggi.

Layanan push merutekan pesan ke klien. Karena push harus diterima oleh klien meskipun aplikasi web masing-masing tidak terbuka di browser, peristiwa push harus didengarkan oleh sesuatu yang terus memantau di latar belakang. Anda dapat menebaknya: Itulah pekerjaan pekerja layanan. Pekerja layanan mendengarkan peristiwa push dan melakukan tugas menampilkan pemberitahuan kepada pengguna.

Jadi, sekarang kita tahu bahwa browser, layanan push, pekerja layanan, dan server aplikasi bekerja secara harmonis untuk mengirim pemberitahuan push kepada pengguna. Mari kita lihat detail implementasinya.

Klien Dorong Web

Meminta izin dari pengguna adalah hal satu kali. Jika pengguna telah memberikan izin untuk menerima pemberitahuan push, kami tidak boleh bertanya lagi. Nilai izin disimpan di Notification.permission .

 /* Notification.permission can have one of these three values: default, granted or denied. */ if (Notification.permission === 'default') { /* The Notification.requestPermission() method shows a notification permission prompt to the user. It returns a promise that resolves to the value of permission*/ Notification.requestPermission().then (result => { if (result === 'denied') { console.log('Permission denied') return } if (result === 'granted') { console.log('Permission granted') /* This means the user has clicked the Allow button. We're to get the subscription token generated by the browser and store it in our database. The subscription token can be fetched using the getSubscription method available on pushManager of the serviceWorkerRegistration object. If subscription is not available, we subscribe using the subscribe method available on pushManager. The subscribe method takes in an object. */ serviceWorkerRegistration.pushManager.getSubscription() .then (subscription => { if (!subscription) { const applicationServerKey = ' ' serviceWorkerRegistration.pushManager.subscribe({ userVisibleOnly: true, // All push notifications from server should be displayed to the user applicationServerKey // VAPID Public key }) } else { saveSubscriptionInDB(subscription, userId) // A method to save subscription token in the database } }) } }) } /* Notification.permission can have one of these three values: default, granted or denied. */ if (Notification.permission === 'default') { /* The Notification.requestPermission() method shows a notification permission prompt to the user. It returns a promise that resolves to the value of permission*/ Notification.requestPermission().then (result => { if (result === 'denied') { console.log('Permission denied') return } if (result === 'granted') { console.log('Permission granted') /* This means the user has clicked the Allow button. We're to get the subscription token generated by the browser and store it in our database. The subscription token can be fetched using the getSubscription method available on pushManager of the serviceWorkerRegistration object. If subscription is not available, we subscribe using the subscribe method available on pushManager. The subscribe method takes in an object. */ serviceWorkerRegistration.pushManager.getSubscription() .then (subscription => { if (!subscription) { const applicationServerKey = ' ' serviceWorkerRegistration.pushManager.subscribe({ userVisibleOnly: true, // All push notifications from server should be displayed to the user applicationServerKey // VAPID Public key }) } else { saveSubscriptionInDB(subscription, userId) // A method to save subscription token in the database } }) } }) }

Dalam metode subscribe di atas, kami meneruskan userVisibleOnly dan applicationServerKey untuk menghasilkan token langganan. Properti userVisibleOnly harus selalu benar karena memberi tahu browser bahwa pemberitahuan push apa pun yang dikirim oleh server akan ditampilkan ke klien. Untuk memahami tujuan applicationServerKey , mari pertimbangkan sebuah skenario.

Jika seseorang mendapatkan ribuan token langganan Anda, mereka dapat mengirimkan pemberitahuan ke titik akhir yang terdapat dalam langganan ini dengan sangat baik. Tidak ada cara bagi titik akhir untuk ditautkan ke identitas unik Anda. Untuk memberikan identitas unik pada token langganan yang dibuat di aplikasi web Anda, kami menggunakan protokol VAPID. Dengan VAPID, server aplikasi secara sukarela mengidentifikasi dirinya ke layanan push saat mengirim pemberitahuan push. Kami menghasilkan dua kunci seperti:

 const webpush = require('web-push') const vapidKeys = webpush.generateVAPIDKeys()

web-push adalah modul npm. vapidKeys akan memiliki satu kunci publik dan satu kunci pribadi. Kunci server aplikasi yang digunakan di atas adalah kunci publik.

Server Dorong Web

Pekerjaan server push web (server aplikasi) sangat mudah. Ini mengirimkan muatan pemberitahuan ke token berlangganan.

 const options = { TTL: 24*60*60, //TTL is the time to live, the time that the notification will be queued in the push service vapidDetails: { subject: '[email protected]', publicKey: ' ', privateKey: ' ' } } const data = { title: 'Update', body: 'Notification sent by the server' } webpush.sendNotification(subscription, data, options) const options = { TTL: 24*60*60, //TTL is the time to live, the time that the notification will be queued in the push service vapidDetails: { subject: '[email protected]', publicKey: ' ', privateKey: ' ' } } const data = { title: 'Update', body: 'Notification sent by the server' } webpush.sendNotification(subscription, data, options) const options = { TTL: 24*60*60, //TTL is the time to live, the time that the notification will be queued in the push service vapidDetails: { subject: '[email protected]', publicKey: ' ', privateKey: ' ' } } const data = { title: 'Update', body: 'Notification sent by the server' } webpush.sendNotification(subscription, data, options)

Ini menggunakan metode sendNotification dari perpustakaan push web.

Pekerja Layanan

Pekerja layanan menunjukkan pemberitahuan kepada pengguna seperti:

 self.addEventListener('push', (event) => { let options = { body: event.data.body, icon: 'images/example.png', } event.waitUntil( /* The showNotification method is available on the registration object of the service worker. The first parameter to showNotification method is the title of notification, and the second parameter is an object */ self.registration.showNotification(event.data.title, options) ) })

Sampai sekarang, kami telah melihat bagaimana pekerja layanan menggunakan cache untuk menyimpan permintaan dan membuat PWA cepat dan andal, dan kami telah melihat bagaimana pemberitahuan push web membuat pengguna tetap terlibat.

Untuk menyimpan banyak data di sisi klien untuk dukungan offline, kami memerlukan struktur data raksasa. Mari kita lihat PWA Financial Times. Anda harus menyaksikan sendiri kekuatan struktur data ini. Muat URL di browser Anda, lalu matikan koneksi internet Anda. Muat ulang halaman. Gan! Apakah masih bekerja? Dia. (Seperti yang saya katakan, offline adalah hitam baru.) Data tidak datang dari kabel. Itu disajikan dari rumah. Buka tab "Aplikasi" di Alat Pengembang Chrome. Di bawah "Penyimpanan", Anda akan menemukan "IndexedDB".

IndexedDB menyimpan data artikel di Financial Times PWA
IndexedDB di Financial Times PWA. (Pratinjau besar)

Lihat toko objek "Artikel", dan perluas salah satu item untuk melihat keajaibannya sendiri. Financial Times telah menyimpan data ini untuk dukungan offline. Struktur data yang memungkinkan kita menyimpan sejumlah besar data ini disebut IndexedDB. IndexedDB adalah database berorientasi objek berbasis JavaScript untuk menyimpan data terstruktur. Kita dapat membuat penyimpanan objek yang berbeda dalam database ini untuk berbagai tujuan. Misalnya, seperti yang dapat kita lihat pada gambar di atas bahwa “Resources”, “ArticleImages”, dan “Articles” disebut sebagai penyimpanan objek. Setiap record dalam penyimpanan objek secara unik diidentifikasi dengan sebuah kunci. IndexedDB bahkan dapat digunakan untuk menyimpan file dan blob.

Mari kita coba memahami IndexedDB dengan membuat database untuk menyimpan buku.

 let openIdbRequest = window.indexedDB.open('booksdb', 1)

Jika database booksdb belum ada, kode di atas akan membuat database booksdb . Parameter kedua untuk metode terbuka adalah versi database. Menentukan versi menangani perubahan terkait skema yang mungkin terjadi di masa mendatang. Misalnya, booksdb sekarang hanya memiliki satu tabel, tetapi ketika aplikasi berkembang, kami bermaksud menambahkan dua tabel lagi ke dalamnya. Untuk memastikan database kami sinkron dengan skema yang diperbarui, kami akan menentukan versi yang lebih tinggi dari yang sebelumnya.

Memanggil metode open tidak langsung membuka database. Ini adalah permintaan asinkron yang mengembalikan objek IDBOpenDBRequest . Objek ini memiliki properti sukses dan kesalahan; kita harus menulis penangan yang sesuai untuk properti ini untuk mengelola status koneksi kita.

 let dbInstance openIdbRequest.onsuccess = (event) => { dbInstance = event.target.result console.log('booksdb is opened successfully') } openIdbRequest.onerror = (event) => { console.log('There was an error in opening booksdb database') } openIdbRequest.onupgradeneeded = (event) => { let db = event.target.result let objectstore = db.createObjectStore('books', { keyPath: 'id' }) }

Untuk mengelola pembuatan atau modifikasi penyimpanan objek (penyimpanan objek dianalogikan dengan tabel berbasis SQL — mereka memiliki struktur nilai kunci), metode onupgradeneeded dipanggil pada objek openIdbRequest . Metode onupgradeneeded akan dipanggil setiap kali versi berubah. Dalam cuplikan kode di atas, kami membuat toko objek buku dengan kunci unik sebagai ID.

Katakanlah, setelah menerapkan potongan kode ini, kita harus membuat satu penyimpanan objek lagi, yang disebut sebagai users . Jadi, sekarang versi database kita adalah 2 .

 let openIdbRequest = window.indexedDB.open('booksdb', 2) // New Version - 2 /* Success and error event handlers remain the same. The onupgradeneeded method gets called when the version of the database changes. */ openIdbRequest.onupgradeneeded = (event) => { let db = event.target.result if (!db.objectStoreNames.contains('books')) { let objectstore = db.createObjectStore('books', { keyPath: 'id' }) } let oldVersion = event.oldVersion let newVersion = event.newVersion /* The users tables should be added for version 2. If the existing version is 1, it will be upgraded to 2, and the users object store will be created. */ if (oldVersion === 1) { db.createObjectStore('users', { keyPath: 'id' }) } }

Kami telah meng-cache dbInstance di event handler sukses dari permintaan terbuka. Untuk mengambil atau menambahkan data di IndexedDB, kami akan menggunakan dbInstance . Mari tambahkan beberapa catatan buku di toko objek buku kita.

 let transaction = dbInstance.transaction('books') let objectstore = transaction.objectstore('books') let bookRecord = { id: '1', name: 'The Alchemist', author: 'Paulo Coelho' } let addBookRequest = objectstore.add(bookRecord) addBookRequest.onsuccess = (event) => { console.log('Book record added successfully') } addBookRequest.onerror = (event) => { console.log('There was an error in adding book record') }

Kami memanfaatkan transactions , terutama saat menulis catatan di toko objek. Transaksi hanyalah pembungkus di sekitar operasi untuk memastikan integritas data. Jika salah satu tindakan dalam transaksi gagal, maka tidak ada tindakan yang dilakukan pada database.

Mari kita ubah record buku dengan metode put :

 let modifyBookRequest = objectstore.put(bookRecord) // put method takes in an object as the parameter modifyBookRequest.onsuccess = (event) => { console.log('Book record updated successfully') }

Mari kita ambil catatan buku dengan metode get :

 let transaction = dbInstance.transaction('books') let objectstore = transaction.objectstore('books') /* get method takes in the id of the record */ let getBookRequest = objectstore.get(1) getBookRequest.onsuccess = (event) => { /* event.target.result contains the matched record */ console.log('Book record', event.target.result) } getBookRequest.onerror = (event) => { console.log('Error while retrieving the book record.') }

Menambahkan Ikon Di Layar Utama

Sekarang hampir tidak ada perbedaan antara PWA dan aplikasi asli, masuk akal untuk menawarkan posisi utama ke PWA. Jika situs web Anda memenuhi kriteria dasar PWA (dihosting di HTTPS, terintegrasi dengan pekerja layanan dan memiliki manifest.json ) dan setelah pengguna menghabiskan beberapa waktu di halaman web, browser akan memanggil prompt di bagian bawah, menanyakan pengguna untuk menambahkan aplikasi ke layar beranda mereka, seperti yang ditunjukkan di bawah ini:

Perintah untuk menambahkan PWA Financial Times di layar beranda
Perintah untuk menambahkan PWA Financial Times di layar beranda. (Pratinjau besar)

Saat pengguna mengklik "Tambahkan FT ke layar Beranda", PWA dapat menginjakkan kakinya di layar beranda, serta di laci aplikasi. Saat pengguna mencari aplikasi apa pun di ponsel mereka, PWA apa pun yang cocok dengan kueri penelusuran akan dicantumkan. Mereka juga akan terlihat di pengaturan sistem, yang memudahkan pengguna untuk mengelolanya. Dalam hal ini, PWA berperilaku seperti aplikasi asli.

PWA menggunakan manifest.json untuk menyediakan fitur ini. Mari kita lihat file manifest.json sederhana.

 { "name": "Demo PWA", "short_name": "Demo", "start_url": "/?standalone", "background_color": "#9F0C3F", "theme_color": "#fff1e0", "display": "standalone", "icons": [{ "src": "/lib/img/icons/xxhdpi.png?v2", "sizes": "192x192" }] }

short_name muncul di layar beranda pengguna dan di pengaturan sistem. name muncul di prompt chrome dan di layar splash. Layar splash adalah apa yang dilihat pengguna saat aplikasi bersiap untuk diluncurkan. start_url adalah layar utama aplikasi Anda. Itulah yang didapat pengguna saat mereka mengetuk ikon di layar beranda. background_color digunakan pada layar splash. theme_color mengatur warna toolbar. Nilai standalone untuk mode display mengatakan bahwa aplikasi akan dioperasikan dalam mode layar penuh (menyembunyikan bilah alat browser). Saat pengguna menginstal PWA, ukurannya hanya dalam kilobyte, bukan megabita aplikasi asli.

Pekerja layanan, pemberitahuan push web, IndexedDB, dan posisi layar beranda melengkapi dukungan offline, keandalan, dan keterlibatan. Perlu dicatat bahwa service worker tidak hidup dan mulai melakukan pekerjaannya pada pemuatan pertama. Pemuatan pertama masih akan lambat hingga semua aset statis dan sumber daya lainnya telah di-cache. Kami dapat menerapkan beberapa strategi untuk mengoptimalkan pemuatan pertama.

Bundling Aset

Semua sumber daya, termasuk HTML, lembar gaya, gambar, dan JavaScript, harus diambil dari server. Semakin banyak file, semakin banyak permintaan HTTPS yang diperlukan untuk mengambilnya. Kami dapat menggunakan bundler seperti WebPack untuk menggabungkan aset statis kami, sehingga mengurangi jumlah permintaan HTTP ke server. WebPack melakukan pekerjaan yang baik untuk mengoptimalkan bundel lebih lanjut dengan menggunakan teknik seperti pemecahan kode (yaitu hanya menggabungkan file-file yang diperlukan untuk pemuatan halaman saat ini, alih-alih menggabungkan semuanya bersama-sama) dan pengocokan pohon (yaitu menghapus dependensi duplikat atau dependensi yang diimpor tetapi tidak digunakan dalam kode).

Mengurangi Perjalanan Pulang Pergi

Salah satu alasan utama kelambatan di web adalah latensi jaringan. Waktu yang diperlukan untuk satu byte untuk melakukan perjalanan dari A ke B bervariasi dengan koneksi jaringan. Misalnya, perjalanan pulang pergi tertentu melalui Wi-Fi membutuhkan waktu 50 milidetik dan 500 milidetik pada koneksi 3G, tetapi 2500 milidetik pada koneksi 2G. Permintaan ini dikirim menggunakan protokol HTTP, yang berarti bahwa sementara koneksi tertentu digunakan untuk permintaan, itu tidak dapat digunakan untuk permintaan lain sampai respons dari permintaan sebelumnya disajikan. Sebuah situs web dapat membuat enam permintaan HTTP asinkron sekaligus karena enam koneksi tersedia ke situs web untuk membuat permintaan HTTP. Rata-rata situs web membuat sekitar 100 permintaan; jadi, dengan maksimal enam koneksi yang tersedia, pengguna mungkin menghabiskan sekitar 833 milidetik dalam satu perjalanan pulang pergi. (Penghitungannya adalah 833 milidetik - 100 6 = 1666. Kita harus membagi 1666 dengan 2 karena kita menghitung waktu yang dihabiskan untuk perjalanan pulang pergi.) Dengan HTTP2, waktu penyelesaian berkurang drastis. HTTP2 tidak memblokir kepala koneksi, sehingga beberapa permintaan dapat dikirim secara bersamaan.

Sebagian besar respons HTTP berisi header etag dan last-modified terakhir. Header yang last-modified adalah tanggal saat file terakhir diubah, dan etag adalah nilai unik berdasarkan konten file. Itu hanya akan berubah ketika isi file diubah. Kedua header ini dapat digunakan untuk menghindari mengunduh file lagi jika versi yang di-cache sudah tersedia secara lokal. Jika browser memiliki versi file ini yang tersedia secara lokal, browser dapat menambahkan salah satu dari dua header ini dalam permintaan seperti:

Tambahkan ETag dan Last-Modified Header untuk mencegah pengunduhan aset cache yang valid
ETag dan Header yang Terakhir Dimodifikasi. (Pratinjau besar)

Server dapat memeriksa apakah isi file telah berubah. If the contents of the file have not changed, then it responds with a status code of 304 ( not modified ).

If-None-Match Header to prevent downloading of valid cached assets
If-None-Match Header. (Pratinjau besar)

This indicates to the browser to use the locally available cached version of the file. By doing all of this, we've prevented the file from being downloaded.

Faster responses are in now place, but our job is not done yet. We still have to parse the HTML, load the style sheets and make the web page interactive. It makes sense to show some empty boxes with a loader to the user, instead of a blank screen. While the HTML document is getting parsed, when it comes across <script src='asset.js'></script> , it will make a synchronous HTTP request to the server to fetch asset.js , and the whole parsing process will be paused until the response comes back. Imagine having a dozen of synchronous static asset references. These could very well be managed just by making use of the async keyword in script references, like <script src='asset.js' async></script> . With the introduction of the async keyword here, the browser will make an asynchronous request to fetch asset.js without hindering the parsing of the HTML. If a script file is required at a later stage, we can defer the downloading of that file until the entire HTML has been parsed. A script file can be deferred by using the defer keyword, like <script src='asset.js' defer></script> .

Kesimpulan

We've learned a lot of many new things that make for a cool web application. Here's a summary of all of the things we've explored in this article:

  1. Service workers make good use of the cache to speed up the loading of assets.
  2. Web push notifications work under the hood.
  3. We use IndexedDB to store a massive amount of data.
  4. Some of the optimizations for instant first load, like using HTTP2 and adding headers like Etag , last-modified and If-None-Match , prevent the downloading of valid cached assets.

That's all, folks!