Menganalisis Performa Jalur Rendering Penting

Dipublikasikan: 31 Maret 2014

Untuk mengidentifikasi dan mengatasi bottleneck kinerja jalur rendering penting, diperlukan pengetahuan yang baik mengenai kesalahan umum. Tur terpandu untuk mengidentifikasi pola performa umum akan membantu Anda mengoptimalkan halaman.

Pengoptimalan jalur rendering penting memungkinkan browser menggambar halaman secepat mungkin: semakin cepat mengubah laman menjadi interaksi yang lebih tinggi, semakin banyak laman yang ditampilkan, dan peningkatan konversi. Untuk meminimalkan waktu yang dihabiskan pengunjung untuk melihat layar kosong, kita perlu mengoptimalkan sumber daya mana yang dimuat dan bagaimana urutannya.

Untuk membantu mengilustrasikan proses ini, mulailah dengan kasus yang sesederhana mungkin dan secara bertahap membangun halaman 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 resource, baik dari cache maupun dari jaringan. Kita akan mengasumsikan hal berikut:

  • Perjalanan bolak-balik jaringan (latensi propagasi) ke server menghabiskan waktu 100 md.
  • Waktu respons server adalah 100 md bagi dokumen HTML dan 10 md bagi 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

Mulai dengan markup HTML dasar dan gambar tunggal; tanpa CSS atau JavaScript. Kemudian, buka panel Jaringan di Chrome DevTools dan periksa waterfall resource yang dihasilkan:

CRP

Seperti yang diharapkan, file HTML memerlukan waktu sekitar 200 md untuk didownload. Perhatikan bahwa bagian transparan pada garis biru menyatakan waktu tunggu browser di jaringan tanpa menerima byte respons sementara bagian yang padat menampilkan waktu untuk menyelesaikan pengunduhan setelah byte respons pertama diterima. Ukuran unduhan HTML kecil (<4 K), jadi kita hanya membutuhkan satu perjalanan bolak balik untuk mengambil file lengkap. Hasilnya, dokumen HTML memerlukan waktu sekitar 200 md untuk diambil, dengan setengah dari waktunya dihabiskan untuk menunggu di jaringan, dan separuh lagi untuk menunggu respons server.

Saat konten HTML tersedia, browser akan mengurai byte, mengonversinya menjadi token, dan membuat hierarki DOM. Perhatikan bahwa DevTools dengan mudah melaporkan waktu untuk peristiwa DOMContentLoaded di bagian bawah (216 md), yang juga dinyatakan dengan garis vertikal biru. Selisih antara akhir download HTML dan garis vertikal biru (DOMContentLoaded) adalah waktu yang dihabiskan browser untuk membuat 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 penting untuk menghasilkan tampilan pertama yang cepat. Sebenarnya, bila membicarakan 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. Ingatlah bahwa peristiwa onload menandai titik ketika semua resource yang diperlukan halaman telah didownload dan diproses; pada titik ini, indikator lingkaran berputar pemuatan dapat berhenti berputar di browser (garis vertikal merah dalam jenjang).

Menambahkan JavaScript dan CSS ke dalam campuran

Laman "pengalaman Hello World" kita tampaknya sederhana, tetapi ada banyak hal yang terjadi di balik layar. Pada praktiknya, kita juga akan memerlukan lebih dari sekadar HTML: kemungkinannya adalah, kita akan memiliki stylesheet CSS dan satu atau beberapa skrip untuk menambahkan beberapa interaktivitas ke halaman. Tambahkan keduanya ke campuran untuk melihat 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:

DOM CRP

Dengan JavaScript dan CSS:

DOM, CSSOM, JS

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

Apa yang terjadi?

  • Tidak seperti contoh HTML biasa, kita juga perlu mengambil dan mem-parse file CSS untuk membangun CSSOM, dan kita membutuhkan DOM maupun CSSOM untuk membangun pohon render.
  • Karena halaman juga berisi file JavaScript pemblokir parser, peristiwa domContentLoaded akan diblokir hingga file CSS didownload dan diuraikan: karena JavaScript mungkin akan membuat kueri CSSOM, maka kita harus memblokir file CSS hingga selesai didownload agar kita bisa mengeksekusi JavaScript.

Bagaimana jika kita mengganti skrip eksternal dengan skrip inline? Meskipun skrip dibuat inline secara langsung ke dalam halaman, browser tidak dapat mengeksekusinya sebelum CSSOM dibangun. Singkatnya, JavaScript yang disisipkan juga merupakan pemblokir parser.

Dengan demikian, meski memblokir CSS, apakah penyisipan skrip secara inline akan membuat render laman menjadi lebih cepat? Coba dan lihat apa yang terjadi.

JavaScript eksternal:

DOM, CSSOM, JS

JavaScript Disisipkan:

DOM, CSSOM, dan JS yang disisipkan

Kita menghilangkan satu permintaan, tetapi waktu onload dan domContentLoaded secara efektif sama. Mengapa? Seperti yang kita ketahui, tidak masalah apakah JavaScript inline atau eksternal, karena begitu browser mencapai tag skrip, browser akan memblokir dan menunggu CSSOM dibangun. Lebih jauh, dalam contoh pertama, browser mengunduh CSS maupun JavaScript secara bersamaan dan selesai mengunduhnya pada waktu yang hampir bersamaan. Dalam hal ini, menyisipkan kode JavaScript secara inline tidak terlalu membantu. Namun, ada beberapa strategi yang dapat membuat halaman kita dirender lebih cepat.

Pertama-tama, ingatlah bahwa semua skrip inline adalah pemblokir parser, tetapi untuk skrip eksternal, kita dapat menambahkan atribut async untuk membuka kunci parser. Urungkan penyisipan dan coba yang itu:

<!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, GCLID, JS

JavaScript Async (eksternal):

DOM, GCLID, JS asinkron

Jauh lebih baik! Peristiwa domContentLoaded dipicu tidak lama setelah HTML diuraikan; browser mengetahui untuk tidak memblokir JavaScript dan karena tidak ada skrip pemblokiran parser lainnya, konstruksi CSSOM juga dapat berlangsung secara pararel.

Atau, kita bisa membuat CSS dan JavaScript secara 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 secara efektif sama seperti dalam contoh sebelumnya; sebagai ganti menandai JavaScript kita sebagai asinkron, kita telah menyisipkan baik CSS dan JS ke dalam halaman itu sendiri. Hal ini membuat halaman HTML kita menjadi jauh lebih besar, tetapi sisi terbaliknya adalah browser tidak harus menunggu mengambil sumber daya eksternal mana pun - semuanya ada di laman itu.

Seperti yang dapat Anda lihat, bahkan dengan halaman yang sangat mendasar, mengoptimalkan jalur rendering penting adalah latihan non-trivial: kita perlu memahami grafik dependensi di antara berbagai resource, kita perlu mengidentifikasi resource mana yang "penting", dan kita harus memilih di antara berbagai strategi berbeda terkait cara menyertakan aset tersebut pada halaman. Tidak ada satu solusi untuk masalah ini; setiap halaman berbeda. Anda perlu mengikuti proses serupa untuk mencari tahu sendiri strategi yang optimal.

Dengan demikian, mari kita lihat apakah kita bisa mundur selangkah dan mengidentifikasi beberapa pola performa umum.

Pola performa

Laman paling sederhana mungkin hanya terdiri dari markup HTML: tanpa CSS, tanpa JavaScript, atau tipe sumber daya lainnya. Untuk merender halaman ini, browser harus memulai permintaan, menunggu dokumen HTML tiba, mengurainya, membangun DOM, kemudian merendernya 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

CRP Halo dunia

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

Sekarang pertimbangkan halaman yang sama, tetapi 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 GCLID

Sekali lagi, kita menghabiskan satu kali bolak-balik jaringan untuk mengambil dokumen HTML, kemudian markup yang diambil akan memberi tahu bahwa kita juga memerlukan file CSS; ini berarti browser harus kembali ke server dan mengambil CSS agar bisa merender laman pada layar. Akibatnya, halaman ini memerlukan minimal dua perjalanan bolak-balik agar dapat ditampilkan. Sekali lagi, file CSS mungkin perlu beberapa kali bolak-balik, sehingga penekanannya adalah "minimum".

Berikut adalah beberapa istilah yang kami gunakan untuk menjelaskan jalur rendering penting:

  • Sumber Daya Penting: Resource yang dapat memblokir rendering awal halaman.
  • Critical Path Length: Jumlah perjalanan bolak-balik, atau total waktu yang diperlukan untuk mengambil semua resource penting.
  • Byte Penting: Jumlah total byte yang diperlukan untuk mencapai rendering pertama halaman, yang merupakan jumlah ukuran file transfer dari semua resource penting. Contoh pertama kita, dengan satu halaman HTML, berisi satu resource penting (dokumen HTML); panjang jalur penting juga sama dengan satu perjalanan bolak-balik jaringan (dengan asumsi file berukuran kecil), dan total byte penting hanyalah ukuran transfer dokumen HTML itu sendiri.

Sekarang bandingkan dengan karakteristik jalur kritis dari contoh HTML dan CSS sebelumnya:

DOM + CSSOM CRP

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

Kita memerlukan HTML maupun CSS untuk membangun pohon render. Akibatnya, HTML dan CSS sama-sama menjadi sumber daya penting: CSS hanya diambil setelah browser mendapatkan dokumen HTML, sehingga panjang jalur pentingnya adalah minimum dua kali bolak-balik. Total jumlah kedua resource menjadi 9 KB byte penting.

Sekarang tambahkan file JavaScript ekstra 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 telah menambahkan app.js, yang merupakan aset JavaScript eksternal di halaman dan resource pemblokiran parser (yang penting). Yang lebih buruk, untuk mengeksekusi file JavaScript, kita harus memblokir dan menunggu CSSOM; ingatlah bahwa JavaScript dapat mengkueri CSSOM dan karena itu browser akan dijeda hingga style.css didownload dan CSSOM dikonstruksikan.

DOM, CSSOM, JavaScript CRP

Dengan demikian, pada praktiknya jika melihat "jenjang jaringan" halaman ini, Anda akan melihat bahwa baik permintaan CSS maupun JavaScript akan dimulai pada waktu yang hampir bersamaan; browser mendapatkan HTML, menemukan kedua resource, dan memulai kedua permintaan. Akibatnya, halaman yang ditampilkan pada gambar sebelumnya memiliki karakteristik jalur kritis berikut:

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

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

Setelah chat dengan developer situs, kita menyadari bahwa JavaScript yang disertakan di halaman kita tidak perlu diblokir; kita memiliki beberapa analisis dan kode lain di dalamnya yang tidak perlu memblokir rendering halaman. Dengan pengetahuan tersebut, kita dapat menambahkan atribut async ke elemen <script> untuk berhenti memblokir 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, RenderScript, CRP JavaScript asinkron

Skrip asinkron memiliki beberapa keunggulan:

  • Skrip tidak lagi merupakan pemblokiran parser dan bukan bagian dari jalur rendering penting.
  • Karena tidak ada skrip penting lainnya, CSS juga tidak perlu memblokir peristiwa domContentLoaded.
  • Semakin cepat peristiwa domContentLoaded diaktifkan, semakin cepat logika aplikasi lain dapat mulai dieksekusi.

Hasilnya, halaman kita yang telah dioptimalkan kini kembali dengan dua sumber daya penting (HTML dan CSS), dengan panjang jalur penting minimum dua perjalanan bolak balik, dan total 9 KB byte penting.

Akhirnya, jika {i>style sheet<i} CSS hanya diperlukan untuk publikasi, 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 JavaScript CRP asinkron

Karena resource style.css hanya digunakan untuk pencetakan, browser tidak perlu diblokir di situ untuk merender halaman. Oleh karena itu, begitu konstruksi DOM selesai, browser memiliki cukup informasi untuk merender halaman. Hasilnya, halaman ini hanya memiliki satu resource penting (dokumen HTML), dan panjang jalur rendering penting minimum adalah satu perjalanan bolak balik.

Masukan