Shell aplikasi adalah HTML, CSS, dan JavaScript minimal yang mendukung antarmuka pengguna. Shell aplikasi harus:
- pemuatan cepat
- disimpan dalam cache
- menampilkan konten secara dinamis
Shell aplikasi adalah rahasia untuk performa yang andal dan baik. Anggap shell aplikasi Anda seperti paket kode yang akan Anda publikasikan ke app store jika Anda mem-build aplikasi native. Shell adalah beban yang diperlukan untuk memulai, tetapi mungkin bukan keseluruhan cerita. Hal ini membuat UI Anda tetap lokal dan mengambil konten secara dinamis melalui API.
Latar belakang
Artikel Progressive Web Apps Alex Russell menjelaskan bagaimana aplikasi web dapat berubah secara progresif melalui penggunaan dan izin pengguna untuk memberikan pengalaman yang lebih mirip aplikasi native lengkap dengan dukungan offline, notifikasi push, dan kemampuan untuk ditambahkan ke layar utama. Hal ini sangat bergantung pada manfaat fungsi dan performa pekerja layanan serta kemampuan penyimpanan dalam cache-nya. Hal ini memungkinkan Anda berfokus pada kecepatan, sehingga aplikasi web Anda memiliki pemuatan instan dan update reguler yang sama seperti yang biasa Anda lihat di aplikasi native.
Untuk memanfaatkan sepenuhnya kemampuan ini, kita memerlukan cara baru untuk memikirkan situs: arsitektur shell aplikasi.
Mari kita pelajari cara membuat struktur aplikasi menggunakan arsitektur shell aplikasi augmentasi pekerja layanan. Kita akan melihat rendering sisi klien dan server serta membagikan contoh menyeluruh yang dapat Anda coba sekarang.
Untuk menekankan poin ini, contoh di bawah menunjukkan pemuatan pertama aplikasi menggunakan arsitektur ini. Perhatikan toast 'Aplikasi siap digunakan secara offline' di bagian bawah layar. Jika update pada shell tersedia nanti, kita dapat memberi tahu pengguna untuk memuat ulang versi baru.
Apa itu pekerja layanan?
Pekerja layanan adalah skrip yang berjalan di latar belakang, terpisah dari halaman web Anda. Layanan ini merespons peristiwa, termasuk permintaan jaringan yang dibuat dari halaman yang ditayangkan dan notifikasi push dari server Anda. Pekerja layanan memiliki masa aktif yang sengaja dibuat singkat. Fungsi ini aktif saat mendapatkan peristiwa dan hanya berjalan selama diperlukan untuk memprosesnya.
Pekerja layanan juga memiliki kumpulan API terbatas jika dibandingkan dengan JavaScript dalam konteks penjelajahan normal. Ini adalah standar untuk pekerja di web. Pekerja layanan tidak dapat mengakses DOM, tetapi dapat mengakses hal-hal seperti Cache API, dan dapat membuat permintaan jaringan menggunakan Fetch API. IndexedDB API dan postMessage() juga tersedia untuk digunakan untuk persistensi data dan pesan antara pekerja layanan dan halaman yang dikontrolnya. Peristiwa push yang dikirim dari server Anda dapat memanggil Notification API untuk meningkatkan interaksi pengguna.
Pekerja layanan dapat mencegat permintaan jaringan yang dibuat dari halaman (yang memicu peristiwa pengambilan di pekerja layanan) dan menampilkan respons yang diambil dari jaringan, atau diambil dari cache lokal, atau bahkan dibuat secara terprogram. Secara efektif, ini adalah proxy yang dapat diprogram di browser. Bagian yang menarik adalah, terlepas dari asal respons, halaman web akan terlihat seolah-olah tidak ada keterlibatan pekerja layanan.
Untuk mempelajari pekerja layanan lebih lanjut, baca Pengantar Pekerja Layanan.
Manfaat performa
Pekerja layanan sangat efektif untuk penyimpanan dalam cache offline, tetapi juga menawarkan peningkatan performa yang signifikan dalam bentuk pemuatan instan untuk kunjungan berulang ke situs atau aplikasi web Anda. Anda dapat meng-cache shell aplikasi agar berfungsi secara offline dan mengisi kontennya menggunakan JavaScript.
Pada kunjungan berulang, hal ini memungkinkan Anda mendapatkan piksel yang bermakna di layar tanpa jaringan, meskipun konten Anda pada akhirnya berasal dari sana. Anggaplah ini sebagai menampilkan toolbar dan kartu segera, lalu memuat konten lainnya secara bertahap.
Untuk menguji arsitektur ini di perangkat sebenarnya, kami telah menjalankan contoh shell aplikasi di WebPageTest.org dan menampilkan hasilnya di bawah.
Pengujian 1: Pengujian dengan Kabel menggunakan Nexus 5 menggunakan Chrome Dev
Tampilan pertama aplikasi harus mengambil semua resource dari jaringan dan tidak mencapai proses rendering yang bermakna hingga 1,2 detik. Berkat penyimpanan dalam cache pekerja layanan, kunjungan berulang kami mencapai proses rendering yang bermakna dan selesai dimuat sepenuhnya dalam 0,5 detik.
Pengujian 2: Pengujian di jaringan 3G dengan Nexus 5 menggunakan Chrome Dev
Kita juga dapat menguji sampel dengan koneksi 3G yang sedikit lebih lambat. Kali ini, waktu yang diperlukan untuk first meaningful paint adalah 2,5 detik pada kunjungan pertama. Perlu waktu 7,1 detik untuk memuat halaman sepenuhnya. Dengan penyimpanan dalam cache pekerja layanan, kunjungan berulang kami mencapai proses rendering yang bermakna dan selesai dimuat sepenuhnya dalam 0,8 detik.
Tampilan lain menceritakan kisah yang serupa. Bandingkan 3 detik yang diperlukan untuk mencapai first meaningful paint di shell aplikasi:
menjadi 0,9 detik yang diperlukan saat halaman yang sama dimuat dari cache pekerja layanan kami. Pengguna akhir kami menghemat waktu lebih dari 2 detik.
Keunggulan performa yang serupa dan andal dapat diperoleh untuk aplikasi Anda sendiri menggunakan arsitektur shell aplikasi.
Apakah pekerja layanan mengharuskan kita untuk memikirkan kembali cara kita menyusun struktur aplikasi?
Pekerja layanan menyiratkan beberapa perubahan halus dalam arsitektur aplikasi. Daripada meringkas semua aplikasi Anda ke dalam string HTML, sebaiknya lakukan hal-hal dengan gaya AJAX. Di sini Anda memiliki shell (yang selalu di-cache dan selalu dapat melakukan booting tanpa jaringan) dan konten yang diperbarui secara berkala dan dikelola secara terpisah.
Implikasi pemisahan ini sangat besar. Pada kunjungan pertama, Anda dapat merender konten di server dan menginstal pekerja layanan di klien. Pada kunjungan berikutnya, Anda hanya perlu meminta data.
Bagaimana dengan progressive enhancement?
Meskipun saat ini pekerja layanan tidak didukung oleh semua browser, arsitektur shell konten aplikasi menggunakan progressive enhancement untuk memastikan semua orang dapat mengakses konten. Misalnya, lihat project contoh kami.
Di bawah ini, Anda dapat melihat versi lengkap yang dirender di Chrome, Firefox Nightly, dan Safari. Di paling kiri, Anda dapat melihat versi Safari tempat konten dirender di server tanpa pekerja layanan. Di sebelah kanan, kita melihat versi Chrome dan Firefox Nightly yang didukung oleh pekerja layanan.
Kapan arsitektur ini sebaiknya digunakan?
Arsitektur shell aplikasi paling sesuai untuk aplikasi dan situs yang dinamis. Jika situs Anda kecil dan statis, Anda mungkin tidak memerlukan shell aplikasi dan dapat menyimpan seluruh situs dalam cache di langkah oninstall
pekerja layanan. Gunakan pendekatan yang paling sesuai untuk project Anda. Sejumlah framework JavaScript sudah mendorong pemisahan logika aplikasi dari konten, sehingga pola ini lebih mudah diterapkan.
Apakah sudah ada aplikasi produksi yang menggunakan pola ini?
Arsitektur shell aplikasi dapat dilakukan hanya dengan beberapa perubahan pada UI aplikasi secara keseluruhan dan telah berfungsi dengan baik untuk situs berskala besar seperti Aplikasi Web Progresif I/O 2015 Google dan Kotak Masuk Google.
Shell aplikasi offline adalah peningkatan performa utama dan juga ditunjukkan dengan baik di aplikasi Wikipedia offline Jake Archibald dan aplikasi web progresif Flipkart Lite.
Menjelaskan arsitektur
Selama pengalaman pemuatan pertama, sasaran Anda adalah menampilkan konten yang bermakna ke layar pengguna secepat mungkin.
Pemuatan pertama dan pemuatan halaman lain
Secara umum, arsitektur shell aplikasi akan:
Prioritaskan pemuatan awal, tetapi izinkan pekerja layanan meng-cache shell aplikasi sehingga kunjungan berulang tidak memerlukan shell untuk diambil kembali dari jaringan.
Muat lambat atau muat latar belakang untuk semua yang lain. Salah satu opsi yang baik adalah menggunakan cache read-through untuk konten dinamis.
Gunakan alat pekerja layanan, seperti sw-precache, misalnya untuk meng-cache dan mengupdate pekerja layanan yang mengelola konten statis Anda dengan andal. (Selengkapnya tentang sw-precache nanti.)
Untuk mencapainya:
Server akan mengirim konten HTML yang dapat dirender klien dan menggunakan header masa berlaku cache HTTP di masa mendatang untuk memperhitungkan browser tanpa dukungan pekerja layanan. Fungsi ini akan menayangkan nama file menggunakan hash untuk mengaktifkan 'pembuatan versi' dan update yang mudah untuk nanti dalam siklus proses aplikasi.
Halaman akan menyertakan gaya CSS inline dalam tag
<style>
dalam dokumen<head>
untuk memberikan tampilan pertama yang cepat dari shell aplikasi. Setiap halaman akan memuat JavaScript yang diperlukan untuk tampilan saat ini secara asinkron. Karena CSS tidak dapat dimuat secara asinkron, kita dapat meminta gaya menggunakan JavaScript karena bersifat asinkron, bukan didorong oleh parser dan sinkron. Kita juga dapat memanfaatkanrequestAnimationFrame()
untuk menghindari kasus saat kita mungkin mendapatkan hit cache yang cepat dan berakhir dengan gaya yang secara tidak sengaja menjadi bagian dari jalur rendering penting.requestAnimationFrame()
memaksa frame pertama untuk digambar sebelum gaya dimuat. Opsi lainnya adalah menggunakan project seperti loadCSS Filament Group untuk meminta CSS secara asinkron menggunakan JavaScript.Pekerja layanan akan menyimpan entri shell aplikasi yang di-cache sehingga pada kunjungan berulang, shell dapat dimuat sepenuhnya dari cache pekerja layanan, kecuali jika update tersedia di jaringan.
Implementasi praktis
Kami telah menulis contoh yang berfungsi penuh menggunakan arsitektur shell aplikasi, JavaScript ES2015 vanilla untuk klien, dan Express.js untuk server. Tentu saja tidak ada yang menghentikan Anda menggunakan stack Anda sendiri untuk bagian klien atau server (misalnya PHP, Ruby, Python).
Siklus proses pekerja layanan
Untuk project shell aplikasi, kita menggunakan sw-precache yang menawarkan siklus proses pekerja layanan berikut:
Acara | Tindakan |
---|---|
Instal | Menyimpan cache shell aplikasi dan resource aplikasi web satu halaman lainnya. |
Aktifkan | Menghapus cache lama. |
Ambil | Menayangkan aplikasi web satu halaman untuk URL dan menggunakan cache untuk aset dan parsial yang telah ditentukan sebelumnya. Gunakan jaringan untuk permintaan lainnya. |
Bit server
Dalam arsitektur ini, komponen sisi server (dalam kasus kami, ditulis dalam Express) harus dapat memperlakukan konten dan presentasi secara terpisah. Konten dapat ditambahkan ke tata letak HTML yang menghasilkan rendering statis halaman, atau dapat ditayangkan secara terpisah dan dimuat secara dinamis.
Dapat dimengerti, penyiapan sisi server Anda mungkin sangat berbeda dengan yang kami gunakan untuk aplikasi demo. Pola aplikasi web ini dapat dicapai oleh sebagian besar penyiapan server, meskipun memerlukan beberapa rancangan ulang. Kami mendapati bahwa model berikut berfungsi dengan cukup baik:
Endpoint ditentukan untuk tiga bagian aplikasi Anda: URL yang ditampilkan kepada pengguna (indeks/karakter pengganti), shell aplikasi (pekerja layanan), dan sebagian HTML Anda.
Setiap endpoint memiliki pengontrol yang menarik tata letak handlebar yang pada akhirnya dapat menarik tampilan dan parsial handlebar. Sederhananya, parsial adalah tampilan yang merupakan potongan HTML yang disalin ke halaman akhir. Catatan: Framework JavaScript yang melakukan sinkronisasi data yang lebih canggih sering kali jauh lebih mudah di-porting ke arsitektur Application Shell. Aplikasi ini cenderung menggunakan data-binding dan sinkronisasi, bukan parsial.
Pengguna awalnya akan melihat halaman statis yang berisi konten. Halaman ini mendaftarkan pekerja layanan, jika didukung, yang meng-cache shell aplikasi dan semua hal yang menjadi dependensinya (CSS, JS, dll.).
Shell aplikasi kemudian akan bertindak sebagai aplikasi web satu halaman, menggunakan JavaScript ke XHR dalam konten untuk URL tertentu. Panggilan XHR dilakukan ke endpoint /partials* yang menampilkan potongan kecil HTML, CSS, dan JS yang diperlukan untuk menampilkan konten tersebut. Catatan: Ada banyak cara untuk melakukannya dan XHR hanyalah salah satunya. Beberapa aplikasi akan menyisipkan datanya (mungkin menggunakan JSON) untuk rendering awal sehingga tidak "statis" dalam arti HTML yang di-flatten.
Browser tanpa dukungan pekerja layanan harus selalu ditayangkan pengalaman penggantian. Dalam demo ini, kita kembali ke rendering sisi server statis dasar, tetapi ini hanyalah salah satu dari banyak opsi. Aspek pekerja layanan memberi Anda peluang baru untuk meningkatkan performa aplikasi gaya Aplikasi Web Satu Halaman menggunakan shell aplikasi yang di-cache.
Pembuatan versi file
Satu pertanyaan yang muncul adalah cara menangani pembuatan versi dan pembaruan file. Hal ini khusus aplikasi dan opsi-opsinya adalah:
Gunakan jaringan terlebih dahulu, dan gunakan versi yang di-cache jika tidak.
Hanya jaringan dan gagal jika offline.
Menyimpan versi lama dalam cache dan mengupdatenya nanti.
Untuk shell aplikasi itu sendiri, pendekatan cache-first harus dilakukan untuk penyiapan pekerja layanan Anda. Jika tidak menyimpan shell aplikasi dalam cache, Anda belum mengadopsi arsitektur dengan benar.
Alat
Kami mengelola sejumlah library helper pekerja layanan yang membuat proses pra-cache shell aplikasi Anda atau menangani pola caching umum lebih mudah disiapkan.
Menggunakan sw-precache untuk shell aplikasi Anda
Menggunakan sw-precache untuk meng-cache shell aplikasi akan menangani masalah seputar revisi file, pertanyaan penginstalan/pengaktifan, dan skenario pengambilan untuk shell aplikasi. Masukkan sw-precache ke dalam proses build aplikasi Anda dan gunakan karakter pengganti yang dapat dikonfigurasi untuk mengambil resource statis Anda. Daripada membuat skrip pekerja layanan secara manual, biarkan sw-precache membuat skrip yang mengelola cache Anda dengan aman dan efisien, menggunakan pengendali pengambilan cache-first.
Kunjungan awal ke aplikasi Anda akan memicu pra-caching kumpulan lengkap resource yang diperlukan. Pengalaman ini mirip dengan pengalaman menginstal aplikasi native dari app store. Saat pengguna kembali ke aplikasi Anda, hanya resource yang diperbarui yang akan didownload. Dalam demo, kami memberi tahu pengguna saat shell baru tersedia dengan pesan, "Update aplikasi. Muat ulang untuk versi baru." Pola ini adalah cara yang mudah untuk memberi tahu pengguna bahwa mereka dapat memuat ulang untuk mendapatkan versi terbaru.
Menggunakan sw-toolbox untuk cache runtime
Gunakan sw-toolbox untuk caching runtime dengan strategi yang bervariasi bergantung pada resource:
cacheFirst untuk gambar, beserta cache bernama khusus yang memiliki kebijakan masa berlaku kustom N maxEntries.
networkFirst atau tercepat untuk permintaan API, bergantung pada kesegaran konten yang diinginkan. Tercepat mungkin tidak masalah, tetapi jika ada feed API tertentu yang sering diperbarui, gunakan networkFirst.
Kesimpulan
Arsitektur shell aplikasi memiliki beberapa manfaat, tetapi hanya masuk akal untuk beberapa class aplikasi. Model ini masih baru dan akan bermanfaat untuk mengevaluasi upaya dan manfaat performa keseluruhan dari arsitektur ini.
Dalam eksperimen kami, kami memanfaatkan berbagi template antara klien dan server untuk meminimalkan pekerjaan membuat dua lapisan aplikasi. Hal ini memastikan progressive enhancement tetap menjadi fitur inti.
Jika Anda sudah mempertimbangkan untuk menggunakan pekerja layanan di aplikasi, lihat arsitekturnya dan evaluasi apakah hal ini sesuai untuk project Anda sendiri.
Terima kasih kepada para peninjau kami: Jeff Posnick, Paul Lewis, Alex Russell, Seth Thompson, Rob Dodson, Taylor Savage, dan Joe Medley.