Pada 2 Februari 2017 yang lalu, Ann Taylor telah menulis artikel 12 Things We Need to See From WordPress Page Builders in 2017 + Who Already Gets It Right di CodeInWP. Kami telah menambah baik Page Builder Sandwich secara berterusan selama lebih daripada setahun sekarang: kami melakukan penyelidikan, mendengar pelanggan kami, jadi kami fikir Page Builder Sandwich akan tersenarai dalam artikel “Who Got it Right” CodeInWP. Namun kami tidak berjaya, disebabkan oleh prestasi. 
Jadi kami bertanya kepada Ann tentang perkara itu, dan beliau membalas:
…Saya mencuba versi percuma sebentar. Ia kelihatan sangat berpotensi: banyak modul, UI yang agak intuitif, pengalaman WYSIWYG yang hebat, semuanya mudah diseret. Namun, apabila saya mempunyai sekitar 10 modul pada halaman, menjadi sukar untuk terus bekerja dengannya disebabkan kelajuan penyuntingan yang perlahan…
Jadi menurut beliau, Page Builder Sandwich mempunyai isu prestasi. Dan memandangkan kami sentiasa mendengar maklum balas, kami menyiasatnya.
Menguji Prestasi
Kami memerlukan cara untuk menyemak prestasi PBS; memandangkan PBS kebanyakannya berjalan pada penyemak imbas melalui Javascript, pemprofil Garis Masa Chrome ialah alat yang tepat untuk tugas tersebut. Pemprofil garis masa boleh mengenal pasti dan mengukur proses yang sedang berlaku dalam halaman web semasa masa jalanan, yang sangat sesuai untuk mencari kekangan kelajuan.
Kami fikir kami patut menguji langkah yang paling mudah dahulu – menggerakkan tetikus kami merentasi pelbagai elemen dan baris. Mudah, bukan? Kami yakin bahawa kami tidak akan menghadapi isu prestasi dengan sesuatu yang semudah ini. Malangnya, kami silap.
Sebaik sahaja kami menekan butang rakam, kami menggerakkan tetikus selama kira-kira 2 saat pada skrin, kemudian menekan butang henti. Berikut ialah garis masa yang terhasil daripada pemprofil:
Dan lihatlah. Ia dipenuhi dengan kekangan prestasi!
Terdapat banyak perkara berlaku dalam graf, tetapi isu yang paling ketara ialah terdapat banyak kawasan ungu di dalamnya (yang sepatutnya tidak berlaku). Selain itu, kawasan ungu ini kelihatan sangat lebar, dan semuanya kelihatan mencecah puncak graf. Anda juga akan perasan bahawa graf itu sentiasa penuh juga, dan kawasan ungu hampir tidak pernah turun ke sifar. Nampaknya penyemak imbas sentiasa sibuk melakukan sesuatu, dan hampir tidak pernah berehat.
Tetapi apakah sebenarnya maksud semua kawasan ungu tersebut?
Pemprofil cukup membantu dengan memberitahu kami bahawa ia dipanggil reflow. Berikut ialah definisi tentang apa itu reflow daripada laman Pembangun Google:
Reflow ialah nama proses penyemak imbas web untuk mengira semula kedudukan dan geometri elemen dalam dokumen.. reflow ialah operasi yang menyekat pengguna dalam penyemak imbas.
Jadi ini bermakna menggerakkan tetikus anda di sekitar PBS mencetuskan sangat banyak reflow. Apa yang lebih buruk ialah setiap reflow menjeda penyemak imbas seketika supaya ia dapat menyelesaikan pengiraannya. Lebih banyak reflow bermakna lebih banyak jeda penyemak imbas, dan pengalamannya menjadi lebih tersekat-sekat (janky). Secara lebih mudah, banyak kawasan ungu = buruk.
Jadi terbukti bahawa Ann betul: kami memang mempunyai kekangan prestasi yang besar, jadi pasukan kami segera bertindak untuk membetulkannya serta-merta.
Menyelesaikan Masalah Reflow / Jank Kami
Sebelum apa-apa lagi, kami perlu membuat kajian untuk mengetahui perkara sebenar yang menyebabkan reflow ini pada mulanya. Kami mendapati bahawa rupanya, banyak perkara biasa dalam Javascript boleh mendorong penyemak imbas menyebabkan reflow.
Hampir mana-mana fungsi yang anda boleh gunakan untuk mendapatkan lebar, tinggi atau kedudukan elemen DOM boleh mencetuskan reflow. Paul Irish menulis senarai yang lengkap bagi semua sifat dan fungsi dalam Javascript yang boleh menyebabkan perkara ini. Berita buruknya ialah Page Builder Sandwich menggunakan banyak sifat dan fungsi ini, yang kesemuanya menyebabkan reflow:
- elem.getBoundingClientRect()
- window.getComputedStyle()
- window.scrollY
- dan banyak lagi…
Penyelesaian #1: Menyimpan dalam Cache Proses Reflow yang Membebankan
Penyelesaian yang paling jelas ialah TIDAK menggunakan sifat dan fungsi tersebut. Walau bagaimanapun, PBS sangat bergantung padanya dan kami tidak boleh meneruskannya tanpa fungsi-fungsi tersebut.
Penyelesaian yang tinggal adalah untuk mengurangkan panggilan kepada fungsi-fungsi tersebut sebanyak mungkin. Bahagian yang sukar di sini ialah kami menggunakan sifat dan fungsi tersebut berkali-kali di pelbagai bahagian PBS, dan kebanyakan masa, kami menggunakannya pada elemen DOM yang sama berulang kali. Untuk membetulkan ini, kami mencipta mekanisme caching kami sendiri bagi setiap sifat/fungsi yang bermasalah.
Sebagai contoh, fungsi getBoundingClientRect memberi kami ukuran piksel semasa untuk bahagian atas, kiri, lebar, tinggi, kanan dan bawah bagi elemen DOM. Kami menggunakan fungsi ini sepanjang masa untuk mengira lokasi di mana untuk melukis garis bentuk elemen dalam PBS. Berikut ialah fungsi caching yang kami cipta untuk tujuan ini:
// Cache: Client rect kami disimpan di sini, kami boleh menggunakan ini untuk mengosongkannya kemudian.
var elemsWithBoundingRects = [];
window.pbsGetBoundingClientRect = function( element ) {
// Semak sama ada kami telah mendapatkan client rect sebelum ini.
if ( ! element._boundingClientRect ) {
// Jika belum, dapatkannya kemudian simpan untuk kegunaan masa hadapan.
element._boundingClientRect = element.getBoundingClientRect();
elemsWithBoundingRects.push( element );
}
return element._boundingClientRect;
};
Daripada memanggil getBoundingClientRect , kami kini menggunakan pbsGetBoundingClientRect . Sekarang setiap kali client rect dikuiri, kami menyimpan nilai tersebut untuk rujukan masa hadapan, sekali gus mengehadkan bilangan pencetus reflow.
Kami juga memerlukan cara untuk mengosongkan cache apabila client rect yang disimpan menjadi tidak sah. Nilai client rect yang disimpan berubah apabila pengguna menatal halaman atau menambah/mengubah/mengalih keluar elemen pada skrin. Jadi kami mencipta fungsi pengosongan cache yang boleh kami panggil setiap kali kami memerlukan nilai client rect yang baharu:
var clearClientRects = function() {
var i;
for ( i = 0; i < elemsWithBoundingRects.length; i++ ) {
if ( elemsWithBoundingRects[ i ] ) {
elemsWithBoundingRects[ i ]._boundingClientRect = null;
}
}
elemsWithBoundingRects = [];
};
Kami juga melakukan mekanisme caching yang sama ini untuk panggilan fungsi lain yang menggunakan banyak sumber yang biasanya kami gunakan.
Penyelesaian #2: Mengelompokkan Operasi Baca & Tulis Bersama Menggunakan FastDom
Melaksanakan penyelesaian #1 meningkatkan prestasi PBS, tetapi ia belum mencukupi.
Mencari maklumat lebih lanjut di Google, kami menemui artikel oleh Wilson Page mengenai Mencegah ‘layout thrashing’. Wilson menyatakan:
Layout Thrashing (reflow) berlaku apabila JavaScript menulis, kemudian membaca dari DOM secara agresif berulang kali, menyebabkan reflow dokumen.
Rupanya, tidak mencukupi untuk mengehadkan panggilan anda kepada sifat dan fungsi khusus yang mencetuskan reflow. Apabila anda melakukan beberapa operasi “baca” dan “tulis” berturut-turut pada DOM, ia juga mencetuskan reflow penyemak imbas. Berikut ialah senario biasa, terus dari laman Wilson:
// Baca
var h1 = element1.clientHeight;
// Tulis (membatalkan reka letak)
element1.style.height = (h1 * 2) + 'px';
// Baca (mencetuskan reflow)
var h2 = element2.clientHeight;
// Tulis (membatalkan reka letak)
element2.style.height = (h2 * 2) + 'px';
// Baca (mencetuskan reflow)
var h3 = element3.clientHeight;
// Tulis (membatalkan reka letak)
element3.style.height = (h3 * 2) + 'px';
// Reflow dokumen pada akhir bingkai
Contoh di atas mencetuskan 3 reflow kerana dimensi keseluruhan halaman menjadi tidak sah selepas operasi tulis. Penyelesaian yang dicadangkan untuk ini adalah dengan menyusun semula kod untuk mengumpulkan semua operasi baca dan tulis bersama-sama supaya keseluruhan proses itu hanya akan mencetuskan 1 reflow penyemak imbas:
// Baca
var h1 = element1.clientHeight;
var h2 = element2.clientHeight;
var h3 = element3.clientHeight;
// Tulis (membatalkan reka letak)
element1.style.height = (h1 * 2) + 'px';
element2.style.height = (h2 * 2) + 'px';
element3.style.height = (h3 * 2) + 'px';
// Reflow dokumen pada akhir bingkai
Coretan kod di atas menjadikan penyelesaian masalah kelihatan mudah. Tetapi hakikatnya, dalam PBS, membaca dan menulis perkara yang mencetuskan reflow berlaku di banyak tempat berbeza pada pelbagai masa sepanjang kitaran hayat PBS. Menyusun semula beribu-ribu baris kod Javascript adalah tidak munasabah.
Mujurlah bagi kami, Wilson turut menyediakan penyelesaian dalam artikelnya. Beliau mencipta skrip utiliti kecil untuk membantu mengumpulkan operasi baca dan tulis tersebut bersama-sama, yang dipanggil FastDom.
Berikut ialah beberapa kod daripada Page Builder Sandwich yang menggunakan FastDom dan juga merangkumi Penyelesaian #1 kami di atas:
// Kumpulkan pembacaan daripada utas lain.
fastdom.measure( function() {
// Gunakan client rect kami yang dicache.
var rect = window.pbsGetBoundingClientRect( elem );
// Kumpulkan penulisan daripada utas 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 ) );
Apa yang FastDom lakukan adalah ia mengelompokkan semua panggilan fastdom.measure dan semua panggilan fastdom.mutate lalu melaksanakannya secara tidak segerak, secara berkesan mengumpulkan semua operasi baca dan tulis bersama untuk mengurangkan semua reflow dalam penyemak imbas.
Penyelesaian #3: Mengakses Objek Gaya Memerlukan Kos yang Tinggi
Kami juga mendapati dengan bantuan jsPerf bahawa beberapa fungsi pengambilan gaya adalah lebih pantas berbanding yang lain.
Dalam PBS, kami biasanya perlu mendapatkan nilai semasa bagi gaya tertentu sesuatu elemen. Sebagai contoh, kami biasanya menggunakan ini untuk mendapatkan jidar atas:
var styles = window.getComputedStyle( elem );
var marginTop = styles.marginTop;
Tetapi, kami mendapati bahawa melakukan ini adalah jauh lebih pantas:
var marginTop = elem.style.marginTop;
Walau bagaimanapun, elem.style.marginTop dan getComputedStyle adalah sangat berbeza. Yang pertama hanya memberikan penggayaan sebaris bagi elemen tersebut, dan tidak mengambil kira sebarang helaian gaya; dan yang kedua mengambil kira kedua-dua gaya sebaris dan helaian gaya.
Tambahan pula, walaupun hasil getComputedStyle telah dicache, ia masih sangat perlahan. Ternyata, mengakses objek gaya itu sendiri adalah proses yang perlahan.
Mujurlah bagi kami, PBS menambahkan gaya sebaris pada elemen, dan memandangkan gaya sebaris diberi keutamaan (kebanyakan masa) berbanding helaian gaya, terdapat kes di mana mendapatkan nilai gaya sebaris sudah mencukupi. Jadi jika kami menggabungkan kedua-dua kaedah di atas, kami boleh menghasilkan kod yang berjalan jauh lebih pantas:
var marginTop = elem.style.marginTop;
if ( ! marginTop ) {
var styles = window.getComputedStyle( elem );
marginTop = styles.marginTop;
}
Hasil Penambahbaikan Kod
3 penyelesaian kami di atas memerlukan banyak penyelidikan, ujian dan pelaksanaan sebenar (serta menangguhkan ciri Fon kami daripada dikeluarkan), tetapi akhirnya semuanya berbaloi.
Kembali kepada ujian awal kami menggerakkan tetikus anda merentasi skrin selama 2 saat. Berikut ialah garis masa lama yang tidak memuaskan/tersekat-sekat:
Dan inilah rupa garis masa baharu kami selepas penambahbaikan prestasi:
Seperti yang anda lihat, terdapat kurang bahagian ungu dalam graf. Dan terdapat perbezaan ketara pada jumlah proses yang dilakukan oleh penyemak imbas. Bahagian segi empat tepat di bahagian bawah juga lebih nipis, yang bermakna proses dijalankan dengan lebih pantas berbanding sebelum ini.
Jika anda membandingkan jumlah kawasan ungu yang tinggi/lebar dalam graf lama dan baharu, kita melihat kepada peningkatan prestasi sebanyak 200% yang menakjubkan! Kami juga melihat peningkatan prestasi secara menyeluruh. Tidak mengecewakan langsung!
Pengajaran & Perkara yang Dipelajari
Sejujurnya, kami tidak menyangka PBS mempunyai isu prestasi sebelum Ann membangkitkannya. Terutamanya kerana kami menggunakan mesin berpusatkan pembangunan dan kekangan prestasi tidak begitu ketara. Mengimbas kembali, menyemak prestasi sepatutnya menjadi salah satu keutamaan utama. Tetapi sekurang-kurangnya sekarang, setakat versi 4.3 ia telah diselesaikan. 
Bagi mereka yang ingin meningkatkan prestasi pemalam WordPress mereka sendiri, terutamanya jika anda juga menggunakan Javascript yang berat, berikut ialah pengajaran saya daripada pengalaman ini:
- Hadkan/simpan dalam cache panggilan anda kepada sifat/fungsi yang mencetuskan reflow. Gunakan ini sebagai panduan: What forces layout/reflow. The comprehensive list.
- Kumpulkan operasi “baca” dan “tulis” kepada gaya DOM bersama-sama. Gunakan FastDom untuk melakukannya untuk anda.
- Gunakan pemprofil Garis Masa Chrome, dan pastikan anda mempunyai sesedikit mungkin reflow (bahagian ungu).