Optimasi load Progressive Web Application (PWA) dengan React JS kurang dari 5 detik dalam koneksi slow 3G
Tulisan ini merupakan bagian dari #WWWIDChallenge
Progressive Web Application (PWA) mulai menjadi bahan perbincangan yang hangat dikalangan penggiat industri teknologi. PWA menggunakan teknologi web yang memberikan pengalaman terbaik kepada pengguna layaknya aplikasi native.
2 bulan yang lalu, WWWID mempublikasikan tantangan untuk membuat aplikasi web yang bisa tampil dan digunakan dalam waktu kurang dari 5 detik dalam koneksi slow 3G. Tantangan tersebut mengajak teman-teman developer untuk membuat sebuah aplikasi RSS reader sederhana dari publikasi WWWID di medium.
Saya ingin menceritakan metode yang saya gunakan dalam membuat Progressive Web Application (PWA) ketika mengikuti tantangan dari WWWID. Saya menggunakan framework React JS berbasis Create-React-App (CRA). CRA secara default telah tersetup service-worker sebagai langkah awal memulai PWA.
Pastikan service-worker telah ter-setup dengan benar.
src/index.js// CRA v1import registerServiceWorker from './registerServiceWorker';
registerServiceWorker();// CRA v2import * as serviceWorker from './serviceWorker';
serviceWorker.register();
Starting Loader
React JS merupakan framework yang cukup gemuk karena memiliki banyak fitur, hal tersebut mengakibatkan starting load-nya agak lambat dan tampilannya akan blank putih sebelum DOM ter-render, hal ini kurang baik dalam user experience. Karena masalah tersebut, diperlukan Starting Loader untuk membuat tampilan loading awal sebelum aplikasi React JS di-render. Tampilan Starting Loader ini akan muncul pada detik pertama.
Saya menaruh Starting Loader ini pada index.html, lalu menghapusnya dalam lifecycle method componentDidMount(). Kamu juga bisa menambahkan kode CSS untuk mempercantik Starting Loader.
public/index.html<head> <style type="text/css">
#startingLoader {
...
}
</style></head>
<body><div id="root"></div><div id="startingLoader">
Wait a second…
</div></body>
src/components/App.jsxcomponentDidMount() {
const elem = document.getElementById('startingLoader');
window.onload = () => {
if (elem) {
elem.remove();
}
};
}
Konfigurasi PWA pada Android dan iOS
Android
Ketika Kamu baru memulai generate aplikasi React menggunakan CRA, dalam folder public secara default telah ter-setup file manifest.json. File ini yang mengontrol bagaimana aplikasi PWA terlihat oleh pengguna, seperti splash screen, app icon, start url dll.
Namun ketika Kamu memulai percobaan menggunakan lighthouse, Ditemukan beberapa kesalahan ketika audit dalam bagian PWA.
Kesalahan pada audit tersebut adalah :
- Does not redirect HTTP traffic to HTTPS.
- User will not be prompted to install the Web App.
- Is not configured for a custom splash screen.
Untuk kesalahan point 1, hal tersebut bisa diatasi dengan redirect ke HTTPS dalam server Kamu. Sedangkan, untuk kesalahan pada point 2 dan 3 terjadi karena secara default dalam manifest.json hanya menyertakan App Icon dengan ukuran 64x64 32x32 24x24 16x16 pixel. Oleh karena itu, Kamu perlu menambahkan ikon dengan ukuran 192x192 dan 512x512 pixel. Selain itu, start_url saya ubah menjadi “/” dari sebelumnya “./index.html” karena dalam konfigurasi router yang saya buat “./index.html” diarahkan ke halaman 404 not found.
Kamu bisa mempelajari tentang web manifest disini.
iOS
Konfigurasi PWA di iOS memerlukan tambahan meta dan link tag seperti dibawah ini yang dicantumkan didalam head tag.
Dalam gist diatas, terdapat <link rel=”apple-touch-startup-image” />, ini menentukan splash screen image pada perangkat iOS. Saya menggunakan media query agar dalam tampil dengan baik di berbagai model perangkat iOS.
Kamu bisa mempelajari konfigurasi PWA di iOS disini.
Code Splitting
Aplikasi web memiliki kecenderungan untuk tumbuh besar karena bertambahnya fitur yang dikembangkan. Semakin lama waktu yang diperlukan untuk memuat aplikasi, semakin membuat frustasi pengguna. Masalah ini semakin kuat dalam lingkungan smartphone dimana koneksi bisa sangat lambat. Untuk itu diperlukan pemecahan kode atau code splitting daripada mem-bundle-nya menjadi 1 file saja, ini memungkinkan hanya memuat kode yang diperlukan saja saat pengguna membutuhkannya.
Kali ini saya menggunakan library react-loadable karena memiliki banyak fitur seperti preloading, delay, timeout dan loading component. Selain itu, ada alternatif lain seperti loadable-components, react-async-component, react-code-splitting. Library tersebut masing-masing mempunyai keunggulan dan kekurangan, Kamu bisa mencoba salah satunya yang bisa memenuhi kebutuhan Kamu.
File javascript dipecah menjadi beberapa chunk file sesuai route, Kamu juga bisa menerapkannya dalam komponent tertentu yang tidak terlihat langsung seperti Modal.
Optimasi Gambar
Sebagian besar konten situs web terdiri dari gambar. Pengoptimalan gambar adalah proses untuk menyaring gambar sehingga mengurangi ukuran total halaman dan load time, juga mengurangi beban jaringan dan penggunaan data (sangat bermanfaat ketika pengguna menggunakan paket data seluler).
Lazy Load Image
Gambar merupakan salah satu konten di aplikasi web yang ukurannya cukup besar, semakin besar dan banyak gambar yang dimuat, semakin lama juga waktu yang diperlukan untuk memuat aplikasi. Sama halnya dengan code splitting, lazy load image juga bertujuan untuk memuat gambar yang diperlukan saja saat gambar tersebut terlihat di viewport.
Saya menggunakan library react-lazy karena implementasinya yang sangat mudah dan menggunakan IntersectionObserver API. Kamu perlu menambahkan polyfill intersection-observer
. Lihat Can I use untuk status browser yang mendukungnya.
import { Lazy } from 'react-lazy';<Lazy ltIE9>
<img src="https://..." alt="..." />
</Lazy>
Dukungan untuk webP
WebP adalah format gambar modern yang menyediakan kompresi lossless dan lossy untuk gambar di web. Menggunakan WebP, Kamu dapat membuat ukuran gambar yang lebih kecil namun tetap menjaga kualitas gambar, sehingga membuat web lebih cepat. Gambar lossless WebP berukuran 26% lebih kecil dibandingkan dengan PNG. Gambar lossy WebP berukuran 25–34% lebih kecil dari gambar JPEG dengan indeks kualitas SSIM yang setara.
Solusi paling mudah untuk mengimplementasikan format webP adalah mengkonversi gambar dari API dengan pihak ketiga, salah satunya adalah Cloudinary.
Namun sayangnya, saat ini WebP hanya didukung oleh Chrome dan Opera, Kamu akan mendapat masalah pada browser di perangkat iOS dan browser Firefox.
Resource Hints (Preload, Prefetch & Preconnect)
Preload
Preload adalah standar web baru yang menawarkan kontrol lebih untuk memuat resources seperti gambar, CSS, JavaScript, dan file font yang akan dimuat dalam suatu halaman web. Umumnya digunakan untuk melakukan pramuat resources yang high priority.
<link rel=”preload” href=”https://fonts.googleapis.com/icon?family=Material+Icons" as=”style”>
Prefetch
Prefetch digunakan untuk mengkontrol resources yang low priority dimana browser mengambil resources yang mungkin diperlukan nanti dalam background process, dan kemudian menyimpannya di cache browser. Ada tiga jenis prefetching, yaitu Link Prefetching, DNS Prefetching, dan Prerendering.
- Link Prefetching
Seperti yang sudah dijelaskan diatas, dengan Link Prefetching browser akan mengambil resources dan menyimpannya di cache browser.
<link rel="prefetch" href="/assets/image.png">
“This technique has the potential to speed up many interactive sites, but won’t work everywhere. For some sites, it’s just too difficult to guess what the user might do next. For others, the data might get stale if it’s fetched too soon. It’s also important to be careful not to prefetch files too soon, or you can slow down the page the user is already looking at. — Google Developers“
2. DNS Prefetching
Prefetch DNS memungkinkan browser untuk melakukan DNS lookups untuk domain eksternal pada halaman web dalam background process saat pengguna sedang menjelajah. Gunakan cara ini untuk domain eksternal yang benar-benar dibutuhkan saja.
<link rel=”dns-prefetch” href=”https://res.cloudinary.com">
“DNS requests are very small in terms of bandwidth, but latency can be quite high, especially on mobile networks. By speculatively prefetching DNS results, latency can be reduced significantly at certain times, such as when the user clicks the link. In some cases, latency can be reduced by a second. — Mozilla Developer Network“
3. Prerendering
Prerendering sangat mirip dengan Link Prefetching karena mengumpulkan resources yang mungkin akan ditelusuri oleh pengguna. Perbedaannya adalah bahwa Prerendering menyajikan seluruh halaman dalam background process, semua aset dokumen.
<link rel="prerender" href="https://www.keycdn.com">
“The
prerender
hint can be used by the application to indicate the next likely HTML navigation target: the user agent will fetch and process the specified resource as an HTML response. To fetch other content-types with appropriate request headers, or if HTML preprocessing is not desired, the application can use theprefetch
hint. – W3C“
Preconnect
Preconnect memungkinkan browser untuk mengatur koneksi awal sebelum permintaan HTTP benar-benar dikirim ke server. Preconnect meliputi DNS lookups, TLS negotiations, TCP handshakes. Hal ini berguna untuk menghilangkan roundtrip latency dan menghemat waktu bagi pengguna.
<link rel="preconnect" href="https://api.rss2json.com" crossorigin>
Saatnya Audit!
Langkah-langkah dalam proses audit aplikasi web :
Deploy ke hosting
Untuk hosting saya memilih Firebase karena penerapannya sangat mudah, mendukung HTTPS, HTTP/2 dan Cache Control. Kamu bisa mempelajari lebih detil tentang Firebase dan menambah project baru disini. Alternatif lain, Kamu dapat menggunakan Heroku, Netlify, Github Pages, Now, Surge dll.
Audit dengan Webpagetest.org
Audit bisa dilakukan dengan dengan mudah menggunakan tools performance test dan lighthouse dari Webpagetest.org.
Lihat detil hasil audit performa dengan webpagetest.org dengan mode Easy, lokasi percobaan Dulles, VA dan koneksi Slow 3G pada tanggal 16 April 2018 disini.
Lihat detil hasil audit lighthouse dengan webpagetest.org dengan lokasi percobaan USA-Virginia pada tanggal 7 Agustus 2018 disini.
Dari hasil percobaan diatas, aplikasi web berhasil dimuat dalam waktu kurang dari 5 detik, tepatnya 3,16 detik, skor PWA 100 dan skor Performance 95.
Project React PWA WWWID
Saya sangat terbuka untuk kontribusi dalam project ini.
Repository GitHub :
Preview Demo :
Terimakasih kepada teman-teman WWWIDPWA yang saling memberikan solusi dalam mengikuti tantangan ini, banyak ilmu yang saya dapatkan semoga grup WWWIDPWA semakin solid dan saling memberi manfaat.