Pemuatan resource Optimize

Dalam modul sebelumnya, beberapa teori di balik jalur rendering kritis telah dieksplorasi, dan bagaimana resource yang memblokir rendering dan memblokir parser dapat menunda rendering awal halaman. Setelah memahami beberapa teori di baliknya, Anda siap mempelajari beberapa teknik untuk mengoptimalkan jalur rendering penting.

Saat halaman dimuat, banyak resource yang dirujuk dalam HTML-nya yang memberikan tampilan dan tata letak halaman melalui CSS, serta interaktivitasnya melalui JavaScript. Dalam modul ini, sejumlah konsep penting terkait resource ini dan pengaruhnya terhadap waktu pemuatan halaman akan dibahas.

Pemblokiran render

Seperti yang dibahas dalam modul sebelumnya, CSS adalah aset pemblokiran render, karena memblokir browser untuk merender konten apa pun hingga CSS Object Model (CSSOM) dibuat. Browser memblokir rendering untuk mencegah Flash of Unstyled Content (FOUC), yang tidak diinginkan dari sudut pandang pengalaman pengguna.

Pada video sebelumnya, ada FOUC singkat yang menampilkan halaman tanpa gaya visual apa pun. Selanjutnya, semua gaya diterapkan setelah CSS halaman selesai dimuat dari jaringan, dan versi halaman yang tidak memiliki gaya langsung diganti dengan versi yang memiliki gaya.

Secara umum, FOUC adalah sesuatu yang biasanya tidak Anda lihat, tetapi konsepnya penting untuk dipahami agar Anda tahu mengapa browser memblokir rendering halaman hingga CSS didownload dan diterapkan ke halaman. Pemblokiran rendering tidak selalu tidak diinginkan, tetapi Anda sebaiknya meminimalkan durasinya dengan mengoptimalkan CSS.

Pemblokiran parser

Resource yang memblokir parser mengganggu parser HTML, seperti elemen <script> tanpa atribut async atau defer. Saat parser menemukan elemen <script>, browser perlu mengevaluasi dan menjalankan skrip sebelum melanjutkan penguraian HTML lainnya. Hal ini sudah didesain seperti itu, karena skrip dapat mengubah atau mengakses DOM selama waktu saat DOM masih dibuat.

<!-- This is a parser-blocking script: -->
<script src="/script.js"></script>

Saat menggunakan file JavaScript eksternal (tanpa async atau defer), parser diblokir sejak file ditemukan hingga didownload, diuraikan, dan dieksekusi. Saat menggunakan JavaScript sebaris, parser juga diblokir hingga skrip sebaris diuraikan dan dieksekusi.

Pemindai pramuat

Pemindai pra-muat adalah pengoptimalan browser dalam bentuk parser HTML sekunder yang memindai respons HTML mentah untuk menemukan dan mengambil resource secara spekulatif sebelum parser HTML utama menemukannya. Misalnya, pemindai pramuat akan memungkinkan browser mulai mendownload resource yang ditentukan dalam elemen <img>, meskipun parser HTML diblokir saat mengambil dan memproses resource seperti CSS dan JavaScript.

Untuk memanfaatkan pemindai pramuat, resource penting harus disertakan dalam markup HTML yang dikirim oleh server. Pola pemuatan resource berikut tidak dapat ditemukan oleh pemindai pramuat:

  • Gambar dimuat oleh CSS menggunakan properti background-image. Referensi gambar ini ada di CSS, dan tidak dapat ditemukan oleh pemindai pramuat.
  • Skrip yang dimuat secara dinamis dalam bentuk markup elemen <script> yang disisipkan ke dalam DOM menggunakan JavaScript atau modul yang dimuat menggunakan import() dinamis.
  • HTML dirender di klien menggunakan JavaScript. Markup tersebut terdapat dalam string di resource JavaScript, dan tidak dapat ditemukan oleh pemindai pemuatan awal.
  • Pernyataan @import CSS.

Pola pemuatan resource ini semuanya adalah resource yang ditemukan terlambat, sehingga tidak diuntungkan dari pemindai pra-muat. Hindari sebisa mungkin. Namun, jika tidak mungkin menghindari pola tersebut, Anda dapat menggunakan petunjuk preload untuk menghindari penundaan penemuan resource.

CSS

CSS menentukan presentasi dan tata letak halaman. Seperti yang dijelaskan sebelumnya, CSS adalah aset pemblokiran render, jadi mengoptimalkan CSS dapat memberikan dampak yang cukup besar pada waktu pemuatan halaman secara keseluruhan.

Minifikasi

Meminifikasi file CSS akan mengurangi ukuran file aset CSS, sehingga lebih cepat didownload. Hal ini dilakukan terutama dengan menghapus konten dari file CSS sumber seperti spasi dan karakter tidak terlihat lainnya, serta menampilkan hasilnya ke file yang baru dioptimalkan:

/* Unminified CSS: */

/* Heading 1 */
h1 {
  font-size: 2em;
  color: #000000;
}

/* Heading 2 */
h2 {
  font-size: 1.5em;
  color: #000000;
}
/* Minified CSS: */
h1,h2{color:#000}h1{font-size:2em}h2{font-size:1.5em}

Dalam bentuknya yang paling dasar, minifikasi CSS adalah pengoptimalan efektif yang dapat meningkatkan FCP situs Anda, dan bahkan LCP dalam beberapa kasus. Alat seperti bundler dapat melakukan pengoptimalan ini secara otomatis untuk Anda dalam build produksi.

Menghapus CSS yang tidak digunakan

Sebelum merender konten apa pun, browser perlu mendownload dan mengurai semua stylesheet. Waktu yang diperlukan untuk menyelesaikan penguraian juga mencakup gaya yang tidak digunakan di halaman saat ini. Jika Anda menggunakan bundler yang menggabungkan semua resource CSS ke dalam satu file, pengguna Anda kemungkinan mendownload lebih banyak CSS daripada yang diperlukan untuk merender halaman saat ini.

Untuk menemukan CSS yang tidak digunakan untuk halaman saat ini, gunakan alat Cakupan di Chrome DevTools.

Screenshot alat cakupan di Chrome DevTools. File CSS dipilih di panel bawahnya, yang menunjukkan sejumlah besar CSS yang tidak digunakan oleh tata letak halaman saat ini.
Alat cakupan di Chrome DevTools berguna untuk mendeteksi CSS (dan JavaScript) yang tidak digunakan oleh halaman saat ini. Hal ini dapat digunakan untuk membagi file CSS menjadi beberapa resource yang akan dimuat oleh halaman yang berbeda, bukan mengirimkan paket CSS yang jauh lebih besar yang dapat menunda rendering halaman.

Menghapus CSS yang tidak digunakan memiliki efek ganda: selain mengurangi waktu download, Anda mengoptimalkan konstruksi pohon render, karena browser perlu memproses lebih sedikit aturan CSS.

Hindari deklarasi CSS @import

Meskipun mungkin tampak praktis, Anda harus menghindari deklarasi @import di CSS:

/* Don't do this: */
@import url('style.css');

Mirip dengan cara kerja elemen <link> di HTML, deklarasi @import di CSS memungkinkan Anda mengimpor resource CSS eksternal dari dalam lembar gaya. Perbedaan utama antara kedua pendekatan ini adalah elemen <link> HTML merupakan bagian dari respons HTML, dan oleh karena itu ditemukan jauh lebih awal daripada file CSS yang didownload oleh deklarasi @import.

Alasannya adalah agar deklarasi @import dapat ditemukan, file CSS yang memuatnya harus didownload terlebih dahulu. Hal ini menghasilkan apa yang disebut sebagai rantai permintaan yang—dalam kasus CSS—menunda berapa lama waktu yang dibutuhkan agar halaman dirender pertama kali. Kekurangan lainnya adalah stylesheet yang dimuat menggunakan deklarasi @import tidak dapat ditemukan oleh pemindai pramuat, dan oleh karena itu menjadi resource yang memblokir rendering yang ditemukan terlambat.

<!-- Do this instead: -->
<link rel="stylesheet" href="style.css">

Dalam sebagian besar kasus, Anda dapat mengganti @import dengan menggunakan elemen <link rel="stylesheet">. Elemen <link> memungkinkan stylesheet didownload secara bersamaan dan mengurangi waktu pemuatan secara keseluruhan, berbeda dengan deklarasi @import, yang mendownload stylesheet secara berurutan.

CSS penting inline

Waktu yang diperlukan untuk mendownload file CSS dapat meningkatkan FCP halaman. Menyisipkan gaya penting dalam dokumen <head> menghilangkan permintaan jaringan untuk resource CSS, dan—jika dilakukan dengan benar—dapat meningkatkan waktu pemuatan awal saat cache browser pengguna tidak siap. CSS yang tersisa dapat dimuat secara asinkron, atau ditambahkan di akhir elemen <body>.

<head>
  <title>Page Title</title>
  <!-- ... -->
  <style>h1,h2{color:#000}h1{font-size:2em}h2{font-size:1.5em}</style>
</head>
<body>
  <!-- Other page markup... -->
  <link rel="stylesheet" href="non-critical.css">
</body>

Di sisi negatifnya, menyisipkan CSS dalam jumlah besar akan menambahkan lebih banyak byte ke respons HTML awal. Karena resource HTML sering kali tidak dapat di-cache dalam waktu yang lama—atau sama sekali tidak dapat di-cache—artinya CSS inline tidak di-cache untuk halaman berikutnya yang mungkin menggunakan CSS yang sama dalam lembar gaya eksternal. Uji dan ukur performa halaman Anda untuk memastikan kompromi yang dilakukan sepadan dengan upaya yang dikeluarkan.

Demo CSS

JavaScript

JavaScript mendorong sebagian besar interaktivitas di web, tetapi ada biayanya. Mengirim terlalu banyak JavaScript dapat membuat halaman web Anda lambat merespons selama pemuatan halaman, dan bahkan dapat menyebabkan masalah responsivitas yang memperlambat interaksi—keduanya dapat membuat pengguna frustrasi.

JavaScript yang memblokir rendering

Saat memuat elemen <script> tanpa atribut defer atau async, browser akan memblokir penguraian dan rendering hingga skrip didownload, diurai, dan dieksekusi. Demikian pula, skrip inline memblokir parser hingga skrip diuraikan dan dieksekusi.

async versus defer

async dan defer memungkinkan skrip eksternal dimuat tanpa memblokir parser HTML, sementara skrip (termasuk skrip inline) dengan type="module" ditangguhkan secara otomatis. Namun, async dan defer memiliki beberapa perbedaan yang penting untuk dipahami.

Penggambaran berbagai mekanisme pemuatan skrip, yang semuanya menjelaskan peran parser, pengambilan, dan eksekusi berdasarkan berbagai atribut yang digunakan seperti async, defer, type=&#39;module&#39;, dan kombinasi ketiganya.
Diambil dari https://html.spec.whatwg.org/multipage/scripting.html

Skrip yang dimuat dengan async diuraikan dan dieksekusi segera setelah didownload, sedangkan skrip yang dimuat dengan defer dieksekusi saat penguraian dokumen HTML selesai—hal ini terjadi pada saat yang sama dengan peristiwa DOMContentLoaded browser. Selain itu, skrip async dapat dijalankan di luar urutan, sedangkan skrip defer dijalankan sesuai urutan kemunculannya dalam markup.

Rendering sisi klien

Secara umum, Anda harus menghindari penggunaan JavaScript untuk merender konten penting atau elemen LCP halaman. Hal ini dikenal sebagai rendering sisi klien, dan merupakan teknik yang banyak digunakan dalam Aplikasi Web Satu Halaman (SPA).

Markup yang dirender oleh JavaScript menghindari pemindai pramuat, karena resource yang ada dalam markup yang dirender klien tidak dapat ditemukan olehnya. Hal ini dapat menunda download resource penting, seperti gambar LCP. Browser hanya mulai mendownload gambar LCP setelah skrip dijalankan, dan menambahkan elemen ke DOM. Selanjutnya, skrip hanya dapat dieksekusi setelah ditemukan, didownload, dan diurai. Hal ini dikenal sebagai rantai permintaan penting dan harus dihindari.

Selain itu, merender markup menggunakan JavaScript lebih cenderung menghasilkan tugas panjang daripada markup yang didownload dari server sebagai respons terhadap permintaan navigasi. Penggunaan rendering HTML sisi klien secara ekstensif dapat berdampak negatif pada latensi interaksi. Hal ini terutama berlaku dalam kasus ketika DOM halaman sangat besar, yang memicu pekerjaan rendering yang signifikan saat JavaScript mengubah DOM.

Minifikasi

Mirip dengan CSS, meminifikasi JavaScript akan mengurangi ukuran file aset skrip. Hal ini dapat menghasilkan download yang lebih cepat, sehingga browser dapat melanjutkan proses parsing dan kompilasi JavaScript dengan lebih cepat.

Selain itu, minifikasi JavaScript selangkah lebih maju daripada minifikasi aset lain, seperti CSS. Saat JavaScript diminifikasi, JavaScript tidak hanya dihapus dari hal-hal seperti spasi, tab, dan komentar, tetapi simbol dalam JavaScript sumber juga dipersingkat. Proses ini terkadang dikenal sebagai uglification. Untuk melihat perbedaannya, ambil kode sumber JavaScript berikut:

// Unuglified JavaScript source code:
export function injectScript () {
  const scriptElement = document.createElement('script');
  scriptElement.src = '/js/scripts.js';
  scriptElement.type = 'module';

  document.body.appendChild(scriptElement);
}

Jika kode sumber JavaScript sebelumnya di-uglify, hasilnya mungkin terlihat seperti cuplikan kode berikut:

// Uglified JavaScript production code:
export function injectScript(){const t=document.createElement("script");t.src="/js/scripts.js",t.type="module",document.body.appendChild(t)}

Dalam cuplikan sebelumnya, Anda dapat melihat bahwa variabel yang mudah dibaca scriptElement dalam sumber disingkat menjadi t. Jika diterapkan di banyak koleksi skrip, penghematan yang dihasilkan bisa cukup signifikan, tanpa memengaruhi fitur yang disediakan JavaScript produksi situs.

Jika Anda menggunakan bundler untuk memproses kode sumber situs, uglifikasi sering kali dilakukan secara otomatis untuk build produksi. Pengubah—seperti Terser, misalnya—juga sangat dapat dikonfigurasi, yang memungkinkan Anda mengubah keagresifan algoritma pengubah untuk mencapai penghematan maksimum. Namun, nilai default untuk alat pengacakan biasanya cukup untuk mencapai keseimbangan yang tepat antara ukuran output dan pelestarian kemampuan.

Demo JavaScript

Menguji pengetahuan Anda

Apa cara terbaik untuk memuat beberapa file CSS di browser?

Deklarasi CSS @import.
Coba lagi.
Beberapa elemen <link>.
Benar.

Apa fungsi pemindai pramuat browser?

Parser HTML sekunder ini memeriksa markup mentah untuk menemukan resource sebelum parser DOM dapat menemukannya agar dapat ditemukan lebih cepat.
Benar.
Mendeteksi elemen <link rel="preload"> dalam resource HTML.
Coba lagi.

Mengapa browser memblokir penguraian HTML secara default untuk sementara saat mendownload resource JavaScript?

Untuk mencegah Flash of Unstyled Content (FOUC).
Coba lagi.
Karena mengevaluasi JavaScript adalah tugas yang sangat intensif CPU, dan menjeda penguraian HTML memberikan lebih banyak bandwidth ke CPU untuk menyelesaikan pemuatan skrip.
Coba lagi.
Karena skrip dapat mengubah atau mengakses DOM.
Benar.

Berikutnya: Membantu browser dengan petunjuk resource

Setelah Anda memahami cara pemuatan resource dalam elemen <head> dapat memengaruhi pemuatan halaman awal dan berbagai metrik, sekarang saatnya melanjutkan. Pada modul berikutnya, petunjuk resource akan dibahas, dan bagaimana petunjuk tersebut dapat memberikan petunjuk berharga kepada browser untuk mulai memuat resource dan membuka koneksi ke server lintas asal lebih cepat daripada yang dilakukan browser tanpa petunjuk tersebut.