Aplikasi Web Pemuatan Instan dengan Arsitektur Shell Aplikasi

Addy Osmani
Addy Osmani

Shell aplikasi adalah HTML, CSS, dan JavaScript minimal yang mendukung antarmuka pengguna. App shell harus:

  • muat dengan cepat
  • disimpan di cache
  • menampilkan konten secara dinamis

Shell aplikasi adalah rahasia untuk menghasilkan performa yang dapat diandalkan. Anggap shell aplikasi Anda seperti paket kode yang akan dipublikasikan ke app store jika Anda membangun aplikasi native. Ini adalah beban yang diperlukan untuk memulai, tetapi mungkin bukan keseluruhan cerita. Library ini membuat UI Anda tetap lokal dan menarik konten secara dinamis melalui API.

Pemisahan Shell Aplikasi dari HTML, JS, dan CSS shell dan Konten HTML

Latar belakang

Artikel Progressive Web App milik Alex Russell menjelaskan bagaimana aplikasi web dapat secara progresif berubah 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 dan kemampuan cachingnya. Hal ini memungkinkan Anda untuk berfokus pada kecepatan, yang memberikan pemuatan instan dan update reguler yang sama seperti yang biasa Anda lihat di aplikasi native pada aplikasi web Anda.

Untuk memanfaatkan kemampuan ini secara maksimal, kita memerlukan cara berpikir baru tentang situs: arsitektur shell aplikasi.

Mari pelajari cara membuat struktur aplikasi menggunakan arsitektur shell aplikasi yang ditambah pekerja layanan. Kita akan melihat rendering sisi klien dan sisi server serta membagikan contoh end-to-end yang dapat Anda coba sekarang.

Untuk menekankan poin ini, contoh di bawah menampilkan pemuatan pertama aplikasi yang menggunakan arsitektur ini. Perhatikan toast 'App is ready for offline use' (Aplikasi siap untuk penggunaan offline) di bagian bawah layar. Jika update shell tersedia nanti, kita dapat memberi tahu pengguna agar memuat ulang untuk versi baru.

Gambar pekerja layanan yang berjalan di DevTools untuk shell aplikasi

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 dilayaninya dan pemberitahuan push dari server Anda. Pekerja layanan sengaja memiliki masa pakai yang singkat. Fungsi ini aktif ketika mendapat peristiwa dan hanya berjalan selama perlu 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 mereka dapat membuat permintaan jaringan menggunakan Fetch API. IndexedDB API dan postMessage() juga tersedia untuk persistensi data dan pengiriman pesan antara pekerja layanan dan halaman yang dikontrolnya. Peristiwa push yang dikirim dari server Anda dapat memanggil Notification API untuk meningkatkan engagement pengguna.

Pekerja layanan dapat mencegat permintaan jaringan yang dibuat dari halaman (yang memicu peristiwa pengambilan pada pekerja layanan) dan menampilkan respons yang diambil dari jaringan, atau diambil dari cache lokal, atau bahkan dibuat secara terprogram. Secara efektif, ini adalah {i>proxy<i} yang dapat diprogram di browser. Bagian rapi adalah, dari mana pun respons itu berasal, 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 membuat cache offline, tetapi juga menawarkan keunggulan 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 menampilkan piksel yang bermakna di layar tanpa jaringan, meskipun konten Anda akhirnya berasal dari sana. Anggap saja seperti menampilkan toolbar dan kartu dengan segera, lalu memuat konten Anda yang lain secara bertahap.

Untuk menguji arsitektur ini pada perangkat sungguhan, kami telah menjalankan contoh shell aplikasi di WebPageTest.org dan menampilkan hasilnya di bawah ini.

Pengujian 1: Menguji di Kabel dengan Nexus 5 menggunakan Chrome Dev

Tampilan pertama aplikasi harus mengambil semua resource dari jaringan dan tidak mencapai paint yang bermakna hingga 1,2 detik masuk. Berkat cache pekerja layanan, kunjungan berulang kami mencapai paint yang bermakna dan sepenuhnya selesai dimuat dalam 0,5 detik.

Diagram Paint Pengujian Halaman Web untuk Koneksi Kabel

Pengujian 2: Menguji di 3G dengan Nexus 5 menggunakan Chrome Dev

Kita juga dapat menguji sampel dengan koneksi 3G yang sedikit lebih lambat. Kali ini, diperlukan 2,5 detik pada kunjungan pertama untuk paint pertama yang bermakna. Perlu waktu 7,1 detik untuk memuat halaman sepenuhnya. Dengan caching pekerja layanan, kunjungan berulang kami mencapai paint yang bermakna dan sepenuhnya selesai dimuat dalam 0,8 detik.

Diagram Paint Pengujian Halaman Web untuk Koneksi 3G

Tampilan lainnya menceritakan kisah yang serupa. Bandingkan 3 detik yang diperlukan untuk mencapai paint pertama yang bermakna di shell aplikasi:

Paint linimasa untuk tampilan pertama dari Web Page Test

ke 0,9 detik yang diperlukan saat halaman yang sama dimuat dari cache pekerja layanan. Waktu lebih dari 2 detik akan disimpan untuk pengguna akhir kami.

Menggambar linimasa untuk tampilan berulang dari Web Page Test

Kemenangan performa yang serupa dan dapat diandalkan mungkin terjadi untuk aplikasi Anda sendiri yang menggunakan arsitektur shell aplikasi.

Apakah pekerja layanan mengharuskan kita untuk memikirkan kembali cara kita membuat struktur aplikasi?

Pekerja layanan menyiratkan beberapa perubahan kecil dalam arsitektur aplikasi. Daripada memadatkan semua aplikasi Anda ke dalam string HTML, ada baiknya melakukan hal-hal bergaya AJAX. Di sinilah Anda memiliki shell (yang selalu di-cache dan selalu dapat melakukan booting tanpa jaringan) dan konten yang dimuat ulang secara teratur dan dikelola secara terpisah.

Implikasi dari pemisahan ini cukup besar. Pada kunjungan pertama, Anda bisa merender konten di server dan menginstal pekerja layanan di klien. Pada kunjungan berikutnya, Anda hanya perlu meminta data.

Bagaimana dengan {i>progressive enhancement<i}?

Meskipun pekerja layanan saat ini tidak didukung oleh semua browser, arsitektur shell konten aplikasi menggunakan progressive enhancement untuk memastikan semua orang dapat mengakses konten. Misalnya, ambil project contoh kita.

Di bawah ini Anda dapat melihat versi lengkap yang dirender di Chrome, Firefox Nightly, dan Safari. Di sebelah kiri, Anda dapat melihat versi Safari tempat konten dirender di server tanpa pekerja layanan. Di sebelah kanan, kita melihat versi Nightly Chrome dan Firefox yang didukung oleh pekerja layanan.

Gambar Application Shell dimuat di Safari, Chrome, dan Firefox

Kapan waktu yang tepat untuk menggunakan arsitektur ini?

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 meng-cache seluruh situs dalam langkah oninstall pekerja layanan. Gunakan pendekatan yang paling masuk akal untuk proyek Anda. Sejumlah framework JavaScript sudah mendorong pemisahan logika aplikasi dari konten, sehingga membuat pola ini lebih mudah untuk diterapkan.

Apakah ada aplikasi produksi yang menggunakan pola ini?

Arsitektur shell aplikasi dapat diterapkan hanya dengan beberapa perubahan pada keseluruhan UI aplikasi dan telah bekerja dengan baik untuk situs berskala besar seperti Progressive Web App milik Google dan Kotak Masuk Google.

Gambar Kotak Masuk Google sedang dimuat. Menggambarkan Kotak Masuk menggunakan pekerja layanan.

Shell aplikasi offline merupakan kemenangan besar dalam performa dan juga ditunjukkan dengan baik dalam aplikasi Wikipedia offline Jake Archibald dan progressive web app Flipkart Lite.

Screenshot Demo Wikipedia Jake Archibald.

Menjelaskan arsitektur

Selama pengalaman pemuatan pertama, tujuan Anda adalah menghadirkan konten yang bermakna ke layar pengguna secepat mungkin.

Pemuatan pertama dan pemuatan halaman lain

Diagram Muatan Pertama dengan App Shell

Secara umum, arsitektur shell aplikasi akan:

  • Prioritaskan pemuatan awal, tetapi biarkan pekerja layanan meng-cache shell aplikasi sehingga kunjungan berulang tidak mengharuskan shell diambil kembali dari jaringan.

  • Pemuatan lambat atau pemuatan latar belakang untuk konten lainnya. Salah satu opsi yang bagus adalah menggunakan cache baca-tayang 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 mencapai hal ini:

  • Server akan mengirim konten HTML yang dapat dirender dan menggunakan header habis masa berlaku cache HTTP yang akan datang untuk memperhitungkan browser tanpa dukungan pekerja layanan. Fungsi ini akan menyajikan nama file menggunakan hash untuk memungkinkan ‘pembuatan versi’ dan update yang mudah untuk nanti dalam siklus proses aplikasi.

  • Halaman akan menyertakan gaya CSS inline di tag <style> dalam dokumen <head> untuk memberikan penggambaran 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 CSS tersebut asinkron, bukan digerakkan oleh parser dan sinkron. Kita juga dapat memanfaatkan requestAnimationFrame() untuk menghindari kasus saat cache ditemukan dengan cepat dan berakhir dengan gaya yang secara tidak sengaja menjadi bagian dari jalur rendering penting. requestAnimationFrame() memaksa frame pertama di-paint sebelum gaya dimuat. Opsi lainnya adalah menggunakan project seperti loadCSS Filament Group untuk meminta CSS secara asinkron menggunakan JavaScript.

  • Service worker akan menyimpan entri yang di-cache dari shell aplikasi sehingga pada kunjungan berulang, shell dapat dimuat sepenuhnya dari cache pekerja layanan kecuali jika update tersedia di jaringan.

App Shell untuk Konten

Implementasi praktis

Kami telah menulis contoh yang berfungsi penuh menggunakan arsitektur shell aplikasi, JavaScript vanilla ES2015 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:

Event Tindakan
Menginstal Meng-cache shell aplikasi dan resource aplikasi web satu halaman lainnya.
Aktifkan Hapus cache lama.
Ambil Sediakan aplikasi web satu halaman untuk URL serta gunakan cache untuk aset dan parsial yang telah ditetapkan. Menggunakan jaringan untuk permintaan lain.

Bit server

Dalam arsitektur ini, komponen sisi server (dalam kasus kita, ditulis dalam Express) harus dapat menangani konten dan presentasi secara terpisah. Konten dapat ditambahkan ke tata letak HTML yang menghasilkan render statis halaman, atau dapat ditayangkan secara terpisah dan dimuat secara dinamis.

Kami dapat memahami bahwa penyiapan sisi server Anda mungkin sangat berbeda dengan yang kami gunakan untuk aplikasi demo. Pola aplikasi web ini dapat dilakukan oleh sebagian besar penyiapan server, meskipun memerlukan proses arsitektur ulang. Kami mendapati bahwa model berikut bekerja dengan cukup baik:

Diagram Arsitektur App Shell
  • Endpoint ditentukan untuk tiga bagian aplikasi Anda: URL yang ditampilkan kepada pengguna (indeks/karakter pengganti), shell aplikasi (pekerja layanan), dan parsial HTML Anda.

  • Setiap endpoint memiliki pengontrol yang menarik tata letak handlebar, yang kemudian dapat menarik tampilan dan parsial setang. Sederhananya, parsial adalah tampilan yang merupakan potongan HTML yang disalin ke dalam halaman akhir. Catatan: Framework JavaScript yang melakukan sinkronisasi data lanjutan sering kali jauh lebih mudah untuk di-port ke arsitektur Application Shell. Mereka cenderung menggunakan data-binding dan sinkronisasi, bukan parsial.

  • Pengguna awalnya melihat halaman statis yang berisi konten. Halaman ini mendaftarkan pekerja layanan, jika didukung, yang meng-cache shell aplikasi dan semua yang bergantung padanya (CSS, JS, dll).

  • Shell aplikasi kemudian akan bertindak sebagai aplikasi web satu halaman, menggunakan javascript ke XHR dalam konten untuk URL tertentu. Panggilan XHR dibuat ke endpoint /Partials* yang menampilkan potongan kecil HTML, CSS, dan JS yang diperlukan untuk menampilkan konten tersebut. Catatan: Ada banyak cara untuk melakukan pendekatan ini, dan XHR hanyalah salah satunya. Beberapa aplikasi akan menyisipkan datanya (mungkin menggunakan JSON) untuk render awal sehingga tidak "statis" dalam arti HTML yang disatukan.

  • Browser tanpa dukungan pekerja layanan harus selalu mendapatkan 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 Satu halaman menggunakan shell aplikasi yang di-cache.

Pembuatan versi file

Satu pertanyaan yang muncul adalah bagaimana menangani pembuatan versi dan pembaruan file. Ini adalah metode spesifik dan opsinya adalah:

  • Jaringan terlebih dahulu dan gunakan versi yang di-cache jika tidak.

  • Hanya jaringan dan gagal jika offline.

  • Simpan versi lama di cache dan perbarui nanti.

Untuk shell aplikasi itu sendiri, pendekatan cache-first harus dilakukan untuk penyiapan pekerja layanan. Jika Anda tidak meng-cache shell aplikasi, Anda belum mengadopsi arsitektur dengan benar.

Alat

Kami mengelola sejumlah library helper pekerja layanan yang membuat proses precaching shell aplikasi atau penanganan pola caching umum lebih mudah disiapkan.

Screenshot Situs Library Pekerja Layanan di Dasar-Dasar Web

Menggunakan sw-precache untuk shell aplikasi Anda

Menggunakan sw-precache untuk meng-cache shell aplikasi akan mengatasi kekhawatiran seputar revisi file, pertanyaan instal/mengaktifkan, dan skenario pengambilan untuk shell aplikasi. Masukkan sw-precache ke dalam proses build aplikasi dan gunakan karakter pengganti yang dapat dikonfigurasi untuk mengambil resource statis. Daripada membuat skrip pekerja layanan secara manual, biarkan sw-precache membuat skrip yang mengelola cache Anda secara aman dan efisien, menggunakan pengendali pengambilan cache terlebih dahulu.

Kunjungan awal ke aplikasi Anda memicu precaching set lengkap resource yang dibutuhkan. Ini serupa dengan pengalaman menginstal aplikasi native dari app store. Saat pengguna kembali ke aplikasi Anda, hanya resource yang diupdate yang akan didownload. Dalam demo, kami memberi tahu pengguna saat shell baru tersedia dengan pesan, "App updates. Muat ulang untuk versi baru." Pola ini adalah cara mudah untuk memberi tahu pengguna bahwa mereka dapat memperbarui untuk versi terbaru.

Menggunakan sw-toolbox untuk menyimpan cache runtime

Gunakan sw-toolbox untuk menyimpan cache runtime dengan berbagai strategi bergantung pada resource:

  • cacheFirst untuk gambar, beserta cache bernama khusus yang memiliki kebijakan akhir masa berlaku kustom N maxEntries.

  • networkFirst atau tercepat untuk permintaan API, bergantung pada keaktualan konten yang diinginkan. Paling cepat mungkin tidak masalah, tetapi jika ada feed API tertentu yang sering diperbarui, gunakan networkFirst.

Kesimpulan

Arsitektur shell aplikasi memiliki beberapa manfaat, tetapi hanya berlaku untuk beberapa class aplikasi. Modelnya masih baru dan perlu mengevaluasi upaya dan manfaat performa secara keseluruhan dari arsitektur ini.

Dalam eksperimen, kami memanfaatkan pembagian template antara klien dan server untuk meminimalkan pekerjaan pembuatan dua lapisan aplikasi. Hal ini memastikan bahwa {i>progressive enhancement<i} masih menjadi fitur inti.

Jika Anda sudah mempertimbangkan untuk menggunakan pekerja layanan di aplikasi, lihat arsitekturnya dan evaluasi apakah sesuai untuk project Anda sendiri.

Terima kasih kepada peninjau kami: Jeff Posnick, Paul Lewis, Alex Russell, Seth Thompson, Rob Dodson, Taylor Savage, dan Joe Medley.