Optimasi startup time, ukuran bundle, animasi 60fps di WebView, gambar & font loading, profiling dengan Chrome DevTools remote & Safari Web Inspector, serta praktik mengurangi cold start dan jank pada aplikasi contoh.

Setelah di episode 15 kita memilih framework UI dan membangun layout mobile, pada episode ini kita fokus ke performance — topik yang sering diabaikan sampai user mulai komplain. Aplikasi yang lambat di mobile akan di-uninstall dalam hitungan detik.
Mengapa performance penting? Karena di mobile, resource terbatas (CPU, RAM, baterai) dan user sangat sensitif terhadap lag. Aplikasi web yang berjalan di WebView memiliki tantangan performance unik dibanding native app.
import { lazy, Suspense } from 'react';
// Load komponen hanya saat dibutuhkan
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}Strategi lain:
<link rel="preload"> untuk font dan CSS kritis.import() untuk route-based splitting.npx vite-bundle-visualizer
# atau untuk webpack
npx webpack-bundle-analyzer stats.json// Jangan import seluruh library
import { format } from 'date-fns'; // tree-shakeable
// Gunakan dynamic import untuk route
const SettingsPage = lazy(() => import('./pages/Settings'));
// Gunakan URL untuk asset besar
const heavyData = new URL('./data.json', import.meta.url);| Kategori | Target |
|---|---|
| App shell | ≤ 200 KB |
| Route per halaman | ≤ 50 KB |
| Total initial load | ≤ 500 KB |
| Image per-load | ≤ 200 KB (compressed) |
.animated-element {
/* Gunakan transform, bukan top/left/margin */
transform: translateY(100px);
transition: transform 0.3s ease-out;
/* Hindari paint yang mahal */
will-change: transform;
}
/* Hindari ini */
.bad-animation {
top: 100px; /* triggering layout */
margin-top: 100px; /* triggering layout */
}function animateScroll(element: HTMLElement, targetY: number) {
const startY = element.scrollTop;
const diff = targetY - startY;
const duration = 300;
let startTime: number | null = null;
function step(timestamp: number) {
if (!startTime) startTime = timestamp;
const progress = Math.min((timestamp - startTime) / duration, 1);
// Easing function
const ease = 1 - Math.pow(1 - progress, 3);
element.scrollTop = startY + diff * ease;
if (progress < 1) {
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
}Tip
Gunakan Chrome DevTools Performance tab untuk merekam animasi dan memeriksa frame rate. Jika frame rate turun di bawah 60fps, cari layout thrashing atau repaint yang mahal.
chrome://inspect/#devices.Dari sini kalian bisa menggunakan Network, Performance, dan Memory tabs untuk profiling.
Safari Web Inspector memberikan akses ke DOM, Console, Network, dan Timeline.
Untuk bottleneck di bridge layer:
Jika profiling menunjukkan bottleneck di native code (bukan JavaScript), kalian mungkin perlu optimasi plugin native — topik yang dibahas di episode 12.
<!-- Gunakan format modern -->
<picture>
<source srcset="image.avif" type="image/avif" />
<source srcset="image.webp" type="image/webp" />
<img src="image.jpg" alt="Description" loading="lazy" />
</picture>/* Preload font kritis */
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-latin.woff2') format('woff2');
font-display: swap;
unicode-range: U+0000-007F; /* Latin only */
}Note
Font loading di WebView bisa menjadi bottleneck pertama. Gunakan font-display: swap agar teks langsung muncul dengan fallback, lalu swap ke custom font saat loaded.
Pada episode 16 ini, kalian telah memahami:
Di episode 17 selanjutnya, kita akan membahas offline-first architecture — cache-first vs network-first, service worker vs storage native, SQLite community plugin, dan praktik fitur CRUD yang tetap berfungsi tanpa jaringan. Sampai jumpa!