Pada tanggal 2 Februari 2017 lalu, Ann Taylor menulis artikel 12 Things We Need to See From WordPress Page Builders in 2017 + Who Already Gets It Right di CodeInWP. Kami telah terus meningkatkan Page Builder Sandwich selama lebih dari setahun sekarang: kami melakukan riset, mendengarkan pelanggan kami, jadi kami mengira Page Builder Sandwich akan masuk ke artikel “Who Got it Right” di CodeInWP. Namun kami ternyata tidak masuk, karena masalah performa.
Jadi kami bertanya kepada Ann tentang hal itu, dan dia menjawab:
…Saya sempat mencoba-coba versi gratisnya. Kelihatannya sangat menjanjikan: banyak modul, UI yang cukup intuitif, pengalaman WYSIWYG yang luar biasa, semuanya mudah digeser. Namun, ketika saya memiliki sekitar 10 modul di halaman, menjadi sulit untuk terus bekerja dengannya karena kecepatan pengeditan yang lambat…
Jadi menurutnya, Page Builder Sandwich memiliki masalah performa. Dan karena kami selalu mendengarkan masukan, kami langsung memeriksanya.
Menguji Performa
Kami memerlukan cara untuk memeriksa performa PBS; karena PBS sebagian besar berjalan di peramban melalui Javascript, Timeline profiler Chrome adalah alat yang tepat untuk pekerjaan ini. Timeline profiler dapat mengidentifikasi dan mengukur proses mana yang berlangsung di halaman web selama runtime, yang sangat cocok untuk menemukan kendala kecepatan (bottleneck).
Kami pikir kami harus menguji langkah yang paling sederhana terlebih dahulu – menggerakkan mouse ke beberapa elemen dan baris. Sederhana, bukan? Kami yakin tidak akan mengalami masalah performa dengan hal sesederhana ini. Sayangnya, kami salah.
Setelah menekan tombol rekam, kami menggerakkan mouse sekitar 2 detik di layar, lalu menekan tombol berhenti. Berikut adalah timeline yang dihasilkan dari profiler:
Dan lihatlah. Isinya penuh dengan bottleneck performa!
Ada banyak hal yang terjadi di grafik tersebut, namun masalah yang paling jelas adalah terdapat banyak area berwarna ungu di dalamnya (yang seharusnya tidak terjadi). Selain itu, area ungu ini terlihat sangat lebar, dan semuanya tampak mencapai puncak grafik. Anda juga akan melihat bahwa grafiknya selalu penuh, dan area ungu tersebut hampir tidak pernah turun ke angka nol. Tampaknya peramban selalu sibuk melakukan sesuatu, dan hampir tidak pernah beristirahat.
Namun apa sebenarnya arti dari semua area ungu tersebut?
Profiler dengan baik hati memberi tahu kami bahwa itu disebut reflow. Berikut adalah definisi dari apa itu reflow dari situs Google Developer:
Reflow adalah nama proses peramban web untuk menghitung ulang posisi dan geometri elemen dalam dokumen.. reflow adalah operasi yang memblokir pengguna (user-blocking) di peramban.
Jadi ini berarti menggerakkan mouse Anda di PBS memicu banyak sekali reflow. Yang lebih buruk lagi adalah setiap reflow menghentikan sementara peramban agar peramban dapat menyelesaikan perhitungannya. Lebih banyak reflow berarti lebih banyak peramban yang terhenti sejenak, dan pengalamannya menjadi semakin tersendat (janky). Secara lebih sederhana, banyak area ungu = buruk.
Jadi ternyata Ann benar: kami memang memiliki kendala performa yang besar, jadi tim kami langsung bergerak untuk memperbaikinya.
Memperbaiki Masalah Reflow / Tersendat Kami
Sebelum melakukan hal lainnya, kami perlu melakukan riset untuk mencari tahu apa yang menyebabkan reflow ini sejak awal. Kami menemukan bahwa ternyata, banyak hal umum di Javascript yang dapat memicu peramban menyebabkan reflow.
Hampir setiap fungsi yang dapat Anda gunakan untuk mendapatkan lebar, tinggi, atau posisi elemen DOM dapat memicu reflow. Paul Irish menulis daftar lengkap tentang semua properti dan fungsi di Javascript yang dapat melakukan ini. Berita buruknya adalah Page Builder Sandwich menggunakan banyak properti dan fungsi ini, yang semuanya menyebabkan reflow:
- elem.getBoundingClientRect()
- window.getComputedStyle()
- window.scrollY
- dan masih banyak lagi…
Perbaikan #1: Menyimpan Cache Proses Reflow yang Berat
Solusi yang paling jelas adalah TIDAK menggunakan properti dan fungsi tersebut. Namun, PBS sangat bergantung padanya dan kami tidak dapat bekerja tanpanya.
Solusi yang tersisa bagi kami adalah memangkas sebanyak mungkin pemanggilan ke fungsi-fungsi tersebut. Bagian sulitnya di sini adalah kami menggunakan properti dan fungsi tersebut berkali-kali di berbagai bagian PBS, dan sebagian besar waktu, kami menggunakannya pada elemen DOM yang sama berkali-kali. Untuk memperbaikinya, kami membuat mekanisme caching kami sendiri untuk setiap properti/fungsi yang bermasalah.
Misalnya, fungsi getBoundingClientRect memberi kami pengukuran piksel atas, kiri, lebar, tinggi, kanan, dan bawah saat ini dari sebuah elemen DOM. Kami menggunakan fungsi ini setiap saat untuk menghitung lokasi tempat menggambar garis luar (outline) elemen di PBS. Berikut fungsi caching yang kami buat untuk ini:
// Cache: Client rect kami disimpan di sini, kami dapat menggunakannya untuk membersihkannya nanti.
var elemsWithBoundingRects = [];
window.pbsGetBoundingClientRect = function( element ) {
// Periksa apakah kami sudah mendapatkan client rect sebelumnya.
if ( ! element._boundingClientRect ) {
// Jika belum, ambil lalu simpan untuk penggunaan berikutnya.
element._boundingClientRect = element.getBoundingClientRect();
elemsWithBoundingRects.push( element );
}
return element._boundingClientRect;
};
Alih-alih memanggil getBoundingClientRect , kami sekarang menggunakan pbsGetBoundingClientRect . Sekarang setiap kali client rect di-query, kami menyimpan nilai tersebut untuk referensi mendatang, sehingga membatasi jumlah pemicu reflow.
Kami juga membutuhkan cara untuk membersihkan cache ketika client rect yang disimpan menjadi tidak valid. Nilai dari client rect yang disimpan berubah saat pengguna menggulir halaman atau menambah/mengubah/menghapus elemen di layar. Jadi kami membuat fungsi pembersihan cache yang dapat kami panggil setiap kali kami membutuhkan nilai client rect yang baru:
var clearClientRects = function() {
var i;
for ( i = 0; i < elemsWithBoundingRects.length; i++ ) {
if ( elemsWithBoundingRects[ i ] ) {
elemsWithBoundingRects[ i ]._boundingClientRect = null;
}
}
elemsWithBoundingRects = [];
};
Kami juga menerapkan mekanisme caching serupa ini untuk pemanggilan fungsi berat lainnya yang biasa kami gunakan.
Perbaikan #2: Mengelompokkan Pembacaan & Penulisan Bersama Menggunakan FastDom
Menerapkan perbaikan #1 meningkatkan performa PBS, tetapi itu belum cukup.
Mencari lebih banyak di Google, kami menemukan sebuah artikel oleh Wilson Page tentang Mencegah ‘layout thrashing’. Wilson mengatakan:
Layout Thrashing (reflow) terjadi ketika JavaScript secara agresif menulis, lalu membaca, dari DOM, berkali-kali yang menyebabkan reflow dokumen.
Rupanya, membatasi pemanggilan Anda ke properti dan fungsi pemicu reflow tertentu saja tidak cukup. Ketika Anda melakukan beberapa operasi “baca” dan “tulis” berturut-turut pada DOM, hal ini juga memicu reflow peramban. Berikut adalah skenario umum, langsung dari situs Wilson:
// Baca var h1 = element1.clientHeight; // Tulis (membatalkan validitas tata letak) element1.style.height = (h1 * 2) + 'px'; // Baca (memicu reflow) var h2 = element2.clientHeight; // Tulis (membatalkan validitas tata letak) element2.style.height = (h2 * 2) + 'px'; // Baca (memicu reflow) var h3 = element3.clientHeight; // Tulis (membatalkan validitas tata letak) element3.style.height = (h3 * 2) + 'px'; // Reflow dokumen di akhir frame
Contoh di atas memicu 3 reflow karena dimensi seluruh halaman menjadi tidak valid setelah operasi tulis. Perbaikan yang disarankan untuk ini adalah menyusun ulang kode untuk mengelompokkan semua pembacaan dan penulisan bersama-sama sehingga semuanya hanya akan memicu 1 reflow peramban:
// Baca var h1 = element1.clientHeight; var h2 = element2.clientHeight; var h3 = element3.clientHeight; // Tulis (membatalkan validitas tata letak) element1.style.height = (h1 * 2) + 'px'; element2.style.height = (h2 * 2) + 'px'; element3.style.height = (h3 * 2) + 'px'; // Reflow dokumen di akhir frame
Cuplikan di atas membuat perbaikan masalah tampak mudah. Namun pada kenyataannya, di PBS, membaca dan menulis hal-hal yang memicu reflow terjadi di banyak tempat berbeda pada waktu yang sangat bervariasi selama siklus hidup PBS. Mengatur ulang ribuan baris kode Javascript bukanlah hal yang memungkinkan.
Beruntung bagi kami, Wilson juga memberikan solusi dalam artikelnya. Dia membuat skrip utilitas kecil untuk membantu mengelompokkan pembacaan dan penulisan tersebut, yang disebut FastDom.
Berikut beberapa kode dari Page Builder Sandwich yang menggunakan FastDom dan juga menyertakan Perbaikan #1 kami di atas:
// Satukan pembacaan dari thread lain.
fastdom.measure( function() {
// Gunakan client rect kami yang telah di-cache.
var rect = window.pbsGetBoundingClientRect( elem );
// Satukan penulisan dari thread lain.
fastdom.mutate( function() {
elem.style.top = ( rect.top + window.pbsScrollY() ) + 'px';
elem.style.height = rect.height + 'px';
elem.style.left = rect.left + 'px';
elem.style.width = rect.width + 'px';
}.bind( this ) );
}.bind( this ) );
Yang dilakukan FastDom adalah mengelompokkan semua pemanggilan fastdom.measure dan semua pemanggilan fastdom.mutate lalu mengeksekusinya secara asinkron, yang secara efektif mengelompokkan semua pembacaan dan penulisan bersama-sama untuk mengurangi semua reflow di peramban.
Perbaikan #3: Mengakses Objek Style Sangat Berat
Kami juga menemukan dengan bantuan jsPerf bahwa beberapa fungsi pengambil style lebih cepat daripada yang lain.
Di PBS, kami biasanya perlu mendapatkan nilai saat ini dari style tertentu pada suatu elemen. Misalnya, kami biasanya menggunakan ini untuk mendapatkan margin atas:
var styles = window.getComputedStyle( elem ); var marginTop = styles.marginTop;
Namun, kami menemukan bahwa melakukan hal ini jauh lebih cepat:
var marginTop = elem.style.marginTop;
Namun, elem.style.marginTop dan getComputedStyle sangat berbeda. Yang pertama hanya memberi kita inline style elemen tersebut, dan tidak memperhitungkan stylesheet apa pun; sedangkan yang kedua memperhitungkan inline style maupun stylesheet.
Terlebih lagi, bahkan ketika hasil getComputedStyle di-cache, prosesnya masih sangat lambat. Ternyata, mengakses objek style itu sendirilah yang merupakan proses yang lambat.
Beruntung bagi kami, PBS menambahkan inline style ke elemen, dan karena inline style memiliki prioritas lebih tinggi (hampir setiap saat) daripada stylesheet, akan ada kasus di mana mengambil nilai inline style saja sudah cukup. Jadi jika kita menggabungkan dua metode di atas, kita bisa menghasilkan kode yang berjalan jauh lebih cepat:
var marginTop = elem.style.marginTop;
if ( ! marginTop ) {
var styles = window.getComputedStyle( elem );
marginTop = styles.marginTop;
}
Hasil Peningkatan Kode
3 perbaikan kami di atas membutuhkan banyak riset, pengujian, dan implementasi nyata (serta menunda perilisan fitur Font kami), namun pada akhirnya semua itu sangat sepadan.
Kembali ke pengujian awal kami dengan menggerakkan mouse di layar selama 2 detik. Berikut timeline lama yang buruk/tersendat:
Dan berikut tampilan timeline baru kami setelah peningkatan performa:
Seperti yang Anda lihat, ada lebih sedikit bagian berwarna ungu pada grafik. Dan ada perbedaan yang nyata pada jumlah proses yang dilakukan oleh peramban. Bagian persegi panjang di bagian bawah juga lebih tipis, yang berarti proses dijalankan lebih cepat dari sebelumnya.
Jika Anda membandingkan jumlah area ungu yang tinggi/lebar pada grafik lama dan baru, kita melihat peningkatan performa sebesar 200% yang luar biasa! Kami juga melihat peningkatan performa secara keseluruhan. Tidak buruk sama sekali!
Poin Penting & Pelajaran yang Didapat
Sejujurnya, kami tidak mengira PBS memiliki masalah performa sebelum Ann mengungkitnya. Terutama karena kami menggunakan mesin khusus pengembangan dan kendala performanya tidak begitu terlihat. Menengok ke belakang, memeriksa performa seharusnya menjadi salah satu prioritas utama. Tapi setidaknya sekarang, per versi 4.3 hal itu sudah ditangani.
Bagi mereka yang ingin meningkatkan performa plugin WordPress sendiri, terutama jika Anda juga menggunakan Javascript yang berat, berikut adalah poin-poin yang saya dapatkan dari pengalaman ini:
- Batasi/cache pemanggilan Anda ke properti/fungsi yang memicu reflow. Gunakan ini sebagai panduan: What forces layout/reflow. The comprehensive list.
- Kelompokkan operasi “baca” dan “tulis” ke style DOM bersama-sama. Gunakan FastDom untuk melakukan ini untuk Anda.
- Gunakan Timeline profiler Chrome, dan pastikan Anda memiliki reflow (bagian ungu) sesedikit mungkin.