Menganalisis Performa Jalur Rendering Penting

Anda memerlukan pengetahuan yang baik tentang kesalahan umum untuk mengidentifikasi dan mengatasi bottleneck performa jalur rendering penting. Mari kita ikuti tur langsung dan mendapatkan pola performa umum yang akan membantu Anda mengoptimalkan halaman.

Dengan mengoptimalkan jalur rendering penting, browser dapat menggambar halaman secepat mungkin: halaman yang lebih cepat menghasilkan engagement yang lebih tinggi, halaman yang lebih banyak dilihat, dan peningkatan konversi. Untuk meminimalkan jumlah waktu yang dihabiskan pengunjung untuk melihat layar kosong, kita perlu mengoptimalkan sumber daya mana yang dimuat dan dalam urutannya.

Untuk membantu menggambarkan proses ini, mari kita mulai dengan kasus yang sesederhana mungkin dan secara bertahap membangun halaman kita untuk menyertakan resource, gaya, dan logika aplikasi tambahan. Dalam prosesnya, kita akan mengoptimalkan setiap kasus; kita juga akan melihat di mana saja kesalahan bisa terjadi.

Sejauh ini kita telah berfokus secara eksklusif pada apa yang terjadi di browser setelah sumber daya (file CSS, JS, atau HTML) tersedia untuk diproses. Kita telah mengabaikan waktu yang diperlukan untuk mengambil sumber daya, baik dari cache maupun dari jaringan. Kami akan mengasumsikan hal berikut:

  • Perjalanan bolak-balik jaringan (latensi propagasi) ke server memerlukan waktu 100 md.
  • Waktu respons server adalah 100 md untuk dokumen HTML dan 10 md untuk semua file lainnya.

Pengalaman halo dunia

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <title>Critical Path: No Style</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
  </body>
</html>

Cobalah

Kita akan mulai dengan markup HTML dasar dan gambar tunggal; tanpa CSS atau JavaScript. Mari kita buka linimasa Jaringan di Chrome DevTools dan memeriksa waterfall resource yang dihasilkan:

CRP

Seperti yang diharapkan, file HTML memerlukan waktu sekitar 200 md untuk didownload. Perhatikan bahwa bagian transparan pada garis biru menggambarkan waktu tunggu browser di jaringan tanpa menerima byte respons, sedangkan bagian yang padat menunjukkan waktu untuk menyelesaikan download setelah byte respons pertama diterima. Ukuran download HTML kecil (<4K), jadi kita hanya membutuhkan satu kali bolak-balik untuk mengambil file lengkap. Akibatnya, dokumen HTML membutuhkan waktu sekitar 200 md untuk diambil, dengan setengah waktu yang dihabiskan untuk menunggu di jaringan dan separuhnya lagi untuk menunggu respons server.

Ketika konten HTML tersedia, browser akan mengurai byte, mengonversinya menjadi token, dan membangun hierarki DOM. Perhatikan bahwa DevTools dengan mudah melaporkan waktu untuk peristiwa DOMContentLoaded di bagian bawah (216 md), yang juga sesuai dengan garis vertikal biru. Jeda antara akhir download HTML dan garis vertikal biru (DOMContentLoaded) adalah waktu yang diperlukan browser untuk membangun hierarki DOM—dalam hal ini, hanya beberapa milidetik.

Perhatikan bahwa "foto keren" kita tidak memblokir peristiwa domContentLoaded. Ternyata, kita dapat membuat hierarki render dan bahkan menggambar halaman tanpa menunggu setiap aset di halaman: tidak semua resource sangat penting untuk menghasilkan penggambaran pertama yang cepat. Sebenarnya, ketika kita berbicara tentang jalur rendering penting, biasanya kita membicarakan markup HTML, CSS, dan JavaScript. Gambar tidak memblokir render awal halaman—meskipun kita juga harus mencoba menggambar gambar sesegera mungkin.

Dengan demikian, peristiwa load (juga dikenal sebagai onload), diblokir pada gambar: DevTools melaporkan peristiwa onload pada 335 md. Ingat bahwa peristiwa onload menandai titik ketika semua resource yang diperlukan halaman telah didownload dan diproses. Pada tahap ini, indikator lingkaran berputar pemuatan dapat berhenti berputar di browser (garis vertikal merah dalam waterfall).

Menambahkan JavaScript dan CSS ke dalam campuran

Halaman "Pengalaman Hello World" kita tampak sederhana, tetapi banyak hal yang terjadi di balik layar. Dalam praktiknya, kita membutuhkan lebih dari sekadar HTML: kemungkinannya adalah, kita akan memiliki stylesheet CSS dan satu atau beberapa skrip untuk menambahkan beberapa interaktivitas ke laman. Mari kita tambahkan keduanya ke campuran dan lihat apa yang terjadi:

<!DOCTYPE html>
<html>
  <head>
    <title>Critical Path: Measure Script</title>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
  </head>
  <body onload="measureCRP()">
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="timing.js"></script>
  </body>
</html>

Cobalah

Sebelum menambahkan JavaScript dan CSS:

CRP DOM

Dengan JavaScript dan CSS:

DOM, Memorystore, JS

Menambahkan file JavaScript dan CSS eksternal akan menambahkan dua permintaan tambahan ke waterfall kita, yang semuanya dikirim pada waktu yang hampir bersamaan oleh browser. Namun, perhatikan bahwa sekarang ada perbedaan waktu yang jauh lebih kecil antara peristiwa domContentLoaded dan onload.

What happened?

  • Tidak seperti contoh HTML biasa, kita juga perlu mengambil dan mengurai file CSS untuk mengonstruksikan WebView, dan kita perlu baik DOM maupun WorkManager untuk membangun pohon render.
  • Karena halaman juga berisi file JavaScript yang memblokir parser, peristiwa domContentLoaded akan diblokir hingga file CSS didownload dan diurai: karena JavaScript mungkin akan membuat kueri YAML, kita harus memblokir file CSS hingga file tersebut didownload sebelum kita dapat mengeksekusi JavaScript.

Bagaimana jika kita mengganti skrip eksternal dengan skrip inline? Bahkan jika skrip dibuat inline secara langsung ke dalam laman, browser tidak bisa mengeksekusinya sebelum WebView dibuat. Singkatnya, JavaScript yang disisipkan juga merupakan pemblokiran parser.

Dengan demikian, meski memblokir CSS, apakah menyisipkan skrip membuat render halaman lebih cepat? Mari kita coba dan lihat apa yang terjadi.

JavaScript Eksternal:

DOM, Memorystore, JS

JavaScript Inline:

DOM, WebView, dan JS inline

Kita mengurangi satu permintaan, tetapi waktu onload dan domContentLoaded kita pada dasarnya sama. Mengapa? Kita tahu bahwa tidak masalah jika JavaScript disisipkan atau eksternal, karena segera setelah browser mencapai tag skrip, browser akan memblokir dan menunggu hingga WebView dibuat. Selanjutnya, dalam contoh pertama, browser mengunduh CSS dan JavaScript secara bersamaan dan selesai mengunduhnya pada waktu yang hampir bersamaan. Dalam contoh ini, menyisipkan kode JavaScript tidak terlalu membantu. Namun ada beberapa strategi yang bisa membuat halaman kita dirender lebih cepat.

Pertama-tama, ingatlah bahwa semua skrip inline merupakan pemblokir parser, tetapi untuk skrip eksternal, kita dapat menambahkan kata kunci "async" untuk membuka pemblokiran parser. Mari kita urungkan penyisipan dan mencobanya:

<!DOCTYPE html>
<html>
  <head>
    <title>Critical Path: Measure Async</title>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
  </head>
  <body onload="measureCRP()">
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script async src="timing.js"></script>
  </body>
</html>

Cobalah

JavaScript yang memblokir parser (eksternal):

DOM, Memorystore, JS

JavaScript (eksternal) asinkron:

DOM, Memorystore, JS asinkron

Jauh lebih baik! Peristiwa domContentLoaded diaktifkan segera setelah HTML diuraikan; browser tahu untuk tidak memblokir JavaScript dan karena tidak ada skrip pemblokiran parser lain, konstruksi WooCommerce juga dapat berlangsung secara paralel.

Atau, kita bisa membuat CSS dan JavaScript menjadi inline:

<!DOCTYPE html>
<html>
  <head>
    <title>Critical Path: Measure Inlined</title>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <style>
      p {
        font-weight: bold;
      }
      span {
        color: red;
      }
      p span {
        display: none;
      }
      img {
        float: right;
      }
    </style>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script>
      var span = document.getElementsByTagName('span')[0];
      span.textContent = 'interactive'; // change DOM text content
      span.style.display = 'inline'; // change CSSOM property
      // create a new element, style it, and append it to the DOM
      var loadTime = document.createElement('div');
      loadTime.textContent = 'You loaded this page on: ' + new Date();
      loadTime.style.color = 'blue';
      document.body.appendChild(loadTime);
    </script>
  </body>
</html>

Cobalah

DOM, CSS inline, JS inline

Perhatikan bahwa waktu domContentLoaded pada dasarnya sama dengan waktu di contoh sebelumnya; alih-alih menandai JavaScript sebagai asinkron, kita telah menyisipkan CSS dan JS ke dalam halaman itu sendiri. Hal ini membuat halaman HTML kita menjadi jauh lebih besar, tetapi sisi baiknya adalah browser tidak perlu menunggu untuk mengambil resource eksternal; semuanya ada di halaman tersebut.

Seperti yang Anda lihat, meskipun dengan halaman yang sangat sederhana, mengoptimalkan jalur rendering penting adalah latihan yang tidak mudah: kita harus memahami grafik ketergantungan antara berbagai resource, kita perlu mengidentifikasi resource mana yang "penting", dan kita harus memilih di antara berbagai strategi berbeda terkait cara menyertakan resource tersebut ke halaman. Tidak ada satu solusi untuk masalah ini; setiap halaman berbeda. Anda perlu mengikuti proses serupa untuk mencari tahu strategi yang optimal.

Meskipun demikian, mari kita lihat apakah kita dapat mundur dan mengidentifikasi beberapa pola performa umum.

Pola performa

Halaman paling sederhana yang mungkin hanya terdiri dari markup HTML; tanpa CSS, tanpa JavaScript, atau tipe sumber daya lainnya. Untuk merender halaman ini, browser harus memulai permintaan, tunggu dokumen HTML tiba, uraikan, bangun DOM, lalu terakhir render di layar:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <title>Critical Path: No Style</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
  </body>
</html>

Cobalah

Halo dunia CRP

Waktu antara T0 dan T1 merekam waktu pemrosesan jaringan dan server. Dalam kasus terbaik (jika file HTML-nya kecil), hanya satu perjalanan bolak-balik jaringan yang akan mengambil seluruh dokumen. Mengingat cara kerja protokol transpor TCP, file yang lebih besar mungkin memerlukan perjalanan bolak-balik lebih banyak. Oleh karena itu, dalam kasus terbaik, halaman di atas memiliki satu jalur rendering penting bolak-balik (minimum).

Sekarang, mari kita pertimbangkan halaman yang sama namun dengan file CSS eksternal:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
  </body>
</html>

Cobalah

DOM + CRP WebView

Sekali lagi, kita harus melakukan perjalanan bolak-balik jaringan untuk mengambil dokumen HTML, lalu markup yang diambil memberi tahu kita bahwa kita juga memerlukan file CSS; ini berarti browser harus kembali ke server dan mendapatkan CSS sebelum dapat merender halaman di layar. Oleh karena itu, halaman ini perlu setidaknya dua kali bolak-balik agar halaman dapat ditampilkan. Sekali lagi, file CSS mungkin memerlukan beberapa perjalanan bolak balik, sehingga penekanannya pada "minimum".

Mari kita definisikan kosakata yang kita gunakan untuk menjelaskan jalur rendering penting:

  • Resource Penting: Resource yang dapat memblokir rendering awal halaman.
  • Panjang Jalur Penting: Jumlah perjalanan bolak-balik, atau total waktu yang diperlukan untuk mengambil semua resource penting.
  • Byte Penting: Jumlah total byte yang diperlukan untuk mendapatkan render halaman pertama, yang merupakan jumlah ukuran file transfer dari semua resource penting. Contoh pertama kami, dengan satu halaman HTML, berisi satu sumber daya penting (dokumen HTML); panjang jalur kritis juga sama dengan satu perjalanan bolak-balik jaringan (dengan asumsi file berukuran kecil), dan total byte penting hanya merupakan ukuran transfer dari dokumen HTML itu sendiri.

Sekarang, mari kita bandingkan dengan karakteristik jalur penting dari contoh HTML + CSS di atas:

DOM + CRP WebView

  • 2 resource penting
  • 2 atau lebih perjalanan bolak-balik untuk panjang jalur kritis minimum
  • 9 KB byte penting

Kita memerlukan HTML dan CSS untuk membangun pohon render. Hasilnya, HTML dan CSS sama-sama merupakan sumber daya penting: CSS hanya diambil setelah browser mendapatkan dokumen HTML, sehingga panjang jalur pentingnya minimal dua kali bolak-balik. Kedua resource ini berjumlah total 9 KB byte penting.

Sekarang, mari tambahkan file JavaScript tambahan ke dalam campuran.

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js"></script>
  </body>
</html>

Cobalah

Kami menambahkan app.js, yang merupakan aset JavaScript eksternal pada halaman dan resource pemblokir parser (yang penting). Yang lebih buruk, untuk mengeksekusi file JavaScript, kita harus memblokir dan menunggu WebView. Ingatlah bahwa JavaScript bisa mengkueri Sandbox dan karena itu browser akan dijeda sampai style.css didownload dan WebView dibuat.

DOM, WebView, JavaScript CRP

Dengan demikian, dalam praktiknya jika kita melihat "Waterfall jaringan" halaman ini, Anda akan melihat bahwa baik permintaan CSS maupun JavaScript dimulai pada waktu yang hampir bersamaan; browser mendapatkan HTML, menemukan kedua sumber daya, dan memulai kedua permintaan tersebut. Akibatnya, halaman di atas memiliki karakteristik jalur kritis berikut:

  • 3 resource penting
  • 2 atau lebih perjalanan bolak-balik untuk panjang jalur kritis minimum
  • 11 KB byte penting

Sekarang kita memiliki tiga resource penting yang berjumlah hingga 11 KB byte penting, tetapi panjang jalur penting kita masih dua kali bolak-balik karena kita dapat mentransfer CSS dan JavaScript secara paralel. Dengan mengetahui karakteristik jalur rendering penting, artinya Anda dapat mengidentifikasi resource penting dan juga memahami cara browser menjadwalkan pengambilannya. Mari lanjutkan dengan contoh kita.

Setelah chat dengan developer situs, kami menyadari bahwa JavaScript yang disertakan pada halaman tidak perlu diblokir; kita memiliki beberapa analisis dan kode lain di dalamnya yang tidak perlu memblokir rendering halaman. Dengan mengetahui hal tersebut, kita dapat menambahkan atribut "async" ke tag skrip untuk membuka pemblokiran parser:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js" async></script>
  </body>
</html>

Cobalah

DOM, Memorystore, CRP JavaScript asinkron

Skrip asinkron memiliki beberapa keuntungan:

  • Skrip tidak lagi memblokir parser dan bukan bagian dari jalur rendering penting.
  • Karena tidak ada skrip penting lainnya, CSS tidak perlu memblokir peristiwa domContentLoaded.
  • Makin cepat peristiwa domContentLoaded diaktifkan, makin cepat pula logika aplikasi lain dapat mulai dieksekusi.

Hasilnya, halaman kita yang telah dioptimalkan kini kembali ke dua resource penting (HTML dan CSS), dengan panjang jalur penting minimum dua roundtrip, dan total 9 KB byte penting.

Terakhir, jika stylesheet CSS hanya perlu dicetak, seperti apa tampilannya?

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" media="print" />
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js" async></script>
  </body>
</html>

Cobalah

DOM, CSS yang tidak memblokir, dan CRP JavaScript asinkron

Karena resource style.css hanya digunakan untuk pencetakan, browser tidak perlu memblokirnya untuk merender halaman. Oleh karena itu, segera setelah konstruksi DOM selesai, browser memiliki cukup informasi untuk merender halaman. Akibatnya, halaman ini hanya memiliki satu sumber daya penting (dokumen HTML), dan panjang jalur rendering penting minimum adalah satu perjalanan bolak balik.

Masukan