Mengapa Performa Dimulai dari Markup HTML?
Banyak pengembang baru memikirkan performa saat aplikasi sudah lambat, lalu berusaha mengoptimalkan kode JavaScript yang rumit. Padahal, keputusan paling menentukan terhadap seberapa cepat layar pertama tampil justru terjadi pada detik pertama peramban membaca berkas HTML Anda.
Tiga metrik pengalaman pengguna utama yang diukur oleh standar industri (Core Web Vitals di web.dev) sangat dipengaruhi oleh cara Anda menulis HTML:
| Metrik Core Web Vitals | Apa yang Diukur? | Target Baik | Peran Langsung Markup HTML |
|---|---|---|---|
| **LCP (Largest Contentful Paint)** | Waktu hingga elemen konten terbesar di layar pertama (biasanya gambar hero atau judul utama) selesai ditampilkan. | ≤ 2,5 detik | Urutan <link>, penggunaan fetchpriority="high" pada gambar utama, dan menghindari loading="lazy" pada gambar teratas. |
| **INP (Interaction to Next Paint)** | Ketanggapan halaman saat pengguna mengeklik atau mengetik hingga perubahan visual berikutnya muncul di layar. | ≤ 200 ms | Memanfaatkan komponen HTML bawaan (<dialog>, <details>, popover) dan mencegah <script> besar memblokir utas utama (main thread). |
| **CLS (Cumulative Layout Shift)** | Kestabilan visual halaman (apakah elemen melompat-lompat secara tiba-tiba saat halaman dimuat). | ≤ 0,1 | Selalu mencantumkan atribut width dan height pada <img>, <video>, dan <iframe>. |
1. Mengendalikan Pemuatan <script>: Biasa vs async vs defer vs type="module"
Ketika parser HTML sedang membaca dokumen dari atas ke bawah lalu menemukan tag <script src="..."> biasa tanpa atribut tambahan, parser wajib berhenti sejenak, mengunduh berkas skrip tersebut, dan menjalankannya sampai selesai sebelum berani melanjutkan membaca baris HTML berikutnya. Mengapa? Karena skrip kuno mungkin memanggil document.write() yang mengubah struktur HTML.
Di era modern, Anda memiliki opsi yang jauh lebih efisien:
1. <script src="app.js"> (Tanpa atribut — Buruk di dalam <head>) HTML Parsing: ████████░░░░░░░░░░░░░░░░░████████████ Unduh JS: ▓▓▓▓▓▓▓ Eksekusi JS: ▒▒▒▒▒▒▒▒▒▒ 2. <script src="analytics.js" async> HTML Parsing: ████████████████░░░░░████████████████ Unduh JS: ▓▓▓▓▓▓▓▓ Eksekusi JS: ▒▒▒▒▒ (Langsung jalan begitu selesai diunduh; urutan tidak dijamin) 3. <script src="app.js" defer> atau <script type="module" src="app.js"> HTML Parsing: █████████████████████████████████████ (Tidak terputus sama sekali!) Unduh JS: ▓▓▓▓▓▓▓▓▓ Eksekusi JS: ▒▒▒▒▒▒ (Jalan berurutan setelah DOM siap)
Aturan Praktis Memilih Atribut <script>
- Gunakan
<script type="module" src="...">atau<script src="..." defer>untuk hampir semua skrip aplikasi Anda:- Berkas diunduh secara paralel di latar belakang tanpa menghentikan pembentukan DOM.
- Eksekusi baru dijalankan setelah seluruh dokumen HTML selesai diurai (tepat sebelum event
DOMContentLoaded), dan urutan eksekusinya dijamin sesuai urutan penulisan tag di HTML.
- Gunakan
asynchanya untuk skrip pihak ketiga yang benar-benar mandiri:- Contoh: skrip analitik ringan yang tidak bergantung pada skrip lain dan tidak perlu menunggu DOM selesai. Jika Anda memiliki
a.jsdanb.jsdengan atributasync, berkas mana pun yang selesai diunduh lebih dulu akan langsung dijalankan lebih dulu!
- Contoh: skrip analitik ringan yang tidak bergantung pada skrip lain dan tidak perlu menunggu DOM selesai. Jika Anda memiliki
2. Pemuatan Malas (loading="lazy") dan Prioritas (fetchpriority)
Aturan Emas loading="lazy" pada <img> dan <iframe>
Atribut loading="lazy" (status Baseline Widely Available) memerintahkan peramban untuk **menunda pengunduhan gambar atau iframe yang berada jauh di bawah layar (below the fold)** sampai pengguna mulai menggulir mendekati elemen tersebut.
Namun, perhatikan satu jebakan yang sangat sering merusak skor LCP:
**🚫 Jangan Pernah Memasang loading="lazy" pada Gambar di Layar Pertama (Above the Fold)!** Jika gambar sampul utama (hero image) di bagian paling atas halaman diberi loading="lazy", peramban akan menahan pengunduhannya sampai tata letak selesai dihitung. Akibatnya, gambar utama justru muncul terlambat!
Sebaliknya, untuk satu gambar utama terpenting di layar pertama (elemen LCP), biarkan pemuatannya eager (bawaan) dan tambahkan atribut fetchpriority="high":
<!-- 1. Gambar Utama di Layar Pertama (Hero / LCP):
JANGAN pakai loading="lazy", dan beri fetchpriority="high" -->
<img
src="/assets/sampul-kelas-utama.avif"
alt="Ilustrasi arsitektur pengembangan web modern"
width="1200"
height="630"
fetchpriority="high"
>
<!-- 2. Gambar-gambar di Bagian Bawah Artikel (Below the Fold):
Gunakan loading="lazy" dan decoding="async" untuk menghemat kuota awal -->
<img
src="/assets/diagram-bab-3.webp"
alt="Diagram alur pembentukan pohon DOM dan CSSOM"
width="800"
height="450"
loading="lazy"
decoding="async"
>
3. Resource Hints: preconnect, dns-prefetch, preload, dan modulepreload
Terkadang peramban baru menyadari bahwa ia membutuhkan sebuah fon web (@font-face) atau gambar latar setelah selesai mengunduh dan membaca berkas CSS. Anda dapat memberi petunjuk lebih awal kepada peramban di dalam <head> menggunakan elemen <link>:
Nilai rel | Fungsi | Kapan Digunakan? |
|---|---|---|
preconnect | Melakukan resolusi DNS, jabat tangan TCP, dan negosiasi TLS lebih awal ke domain pihak ketiga. | Ketika halaman pasti akan mengambil aset penting (seperti fon atau API) dari domain lain dalam beberapa detik pertama. Batasi maksimal 2–4 domain agar tidak membebani CPU/jaringan. |
dns-prefetch | Hanya melakukan resolusi DNS di latar belakang. | Cadangan ringan untuk domain pihak ketiga tambahan yang tidak terlalu kritis. |
preload | Memaksa peramban segera mengunduh satu aset kritis yang dibutuhkan untuk perenderan layar pertama. | Untuk berkas fon utama (.woff2) atau gambar LCP yang tersembunyi di dalam CSS. Wajib menyertakan atribut as="..." (dan crossorigin untuk fon)! |
modulepreload | Mengunduh dan mengurai modul ES (type="module") lebih awal ke dalam module map. | Untuk mempercepat rantai impor modul JavaScript utama aplikasi. |
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Optimasi Pemuatan — Kodeloka</title>
<!-- 1. Buka koneksi awal ke server CDN aset statis -->
<link rel="preconnect" href="https://cdn.asadinedu.com" crossorigin>
<!-- 2. Preload fon utama yang pasti dipakai di layar pertama (wajib ada as="font" dan crossorigin) -->
<link
rel="preload"
href="/assets/fonts/plus-jakarta-sans-v8-latin.woff2"
as="font"
type="font/woff2"
crossorigin
>
<link rel="stylesheet" href="/assets/learning.css">
<script type="module" src="/assets/learning.js"></script>
</head>
preloadJangan mem-preload semuanya! Jika Anda menandai 10 berkas sebagai preload, maka tidak ada lagi yang benar-benar menjadi prioritas dan jaringan pengguna justru macet. Selain itu, jika Anda mem-preload sebuah berkas tetapi berkas tersebut tidak digunakan dalam waktu beberapa detik setelah halaman dimuat, peramban akan mencetak peringatan di konsol karena Anda membuang kuota data pengguna.
Latihan Praktik: Mengoptimalkan Halaman Artikel yang Lambat
Bayangkan sebuah halaman artikel yang saat ini memiliki masalah performa berikut:
- Memuat
<script src="app.js"></script>di baris pertama<head>tanpa atribut. - Memasang
loading="lazy"pada semua gambar termasuk banner utama di bagian paling atas halaman. - Tidak mencantumkan
widthdanheightpada 5 gambar ilustrasi di dalam artikel.
Tulislah berkas artikel-cepat.html yang memperbaiki ketiga masalah di atas serta menambahkan <link rel="preload"> yang benar untuk satu berkas fon .woff2 lokal!