2017-yil 2-fevralda Ann Teylor CodeInWP saytida 12 Things We Need to See From WordPress Page Builders in 2017 + Who Already Gets It Right maqolasini yozgan edi. Biz Page Builder Sandwich ustida bir yildan ortiq vaqtdan beri tinimsiz ish olib bormoqdamiz: izlanishlar o‘tkazamiz, mijozlarimizni tinglaymiz, shuning uchun Page Builder Sandwich CodeInWP’ning «Buni kim to‘g‘ri bajargan» («Who Got it Right») maqolasiga kiradi deb o‘ylagan edik. Biroq unumdorlik sababli kira olmadik. 
Shuning uchun biz bu haqda Anndan so‘radik va u shunday javob berdi:
…Men bepul versiyasini biroz sinab ko‘rdim. U katta umid uyg‘otadi: ko‘plab modullar, ancha tushunarli UI, ajoyib WYSIWYG tajribasi, hamma narsa osongina suriladi. Biroq sahifada 10 ga yaqin modul paydo bo‘lgach, tahrirlash tezligi pastligi sababli u bilan ishlashni davom ettirish qiyinlashdi…
Demak, uning so‘zlariga ko‘ra, Page Builder Sandwich’da unumdorlik muammolari mavjud. Biz doimo fikr-mulohazalarga quloq solganimiz sababli, buni tekshirib ko‘rdik.
Unumdorlikni tekshirish
Bizga PBS unumdorligini tekshirish usuli kerak edi; PBS asosan brauzerda JavaScript orqali ishlagani sababli, bu vazifa uchun Chrome’ning Timeline profayleri eng to‘g‘ri vosita bo‘ldi. Timeline profayleri veb-sahifaning ishlashi davomida qaysi jarayonlar yuz berayotganini aniqlay va o‘lchay oladi, bu esa tezlik to‘siqlarini topish uchun ayni muddao.
Biz avval eng oddiy amallarni sinab ko‘rishga qaror qildik – sichqonchani bir nechta elementlar va qatorlar bo‘ylab harakatlantirish. Oddiy, shunday emasmi? Shunday oddiy narsada unumdorlik muammolariga duch kelmaymiz, deb ishonchimiz komil edi. Afsuski, adashgan ekanmiz.
Yozib olish tugmasini bosgach, sichqonchani ekranda taxminan 2 soniya yurgizdik, so‘ng to‘xtatish tugmasini bosdik. Mana profayler natijasida olingan vaqt jadvali:
Mana, ko‘ring. U unumdorlik to‘siqlariga to‘la!
Grafikda juda ko‘p narsa sodir bo‘lmoqda, biroq eng yaqqol muammo – unda binafsharang sohalarning juda ko‘pligidir (aslida bunday bo‘lmasligi kerak). Buning ustiga, ushbu binafsharang sohalar ko‘rinib turganidek juda keng va barchasi grafikning eng yuqori cho‘qqisiga yetayotgandek. Shuningdek, grafik hamisha to‘la ekanligini va binafsharang sohalar deyarli hech qachon nolga tushmasligini sezasiz. Brauzer doimo biror narsa bilan band va deyarli dam olmayotganga o‘xshaydi.
Ammo bu binafsharang sohalar aslida nimani anglatadi?
Profayler bizga ularning reflow deb atalishini ko‘rsatdi. Mana Google Developer saytidan:
Reflow – bu veb-brauzerning hujjatdagi elementlar o‘rni va geometriyasini qayta hisoblash jarayonining nomi.. reflow brauzerda foydalanuvchini bloklovchi operatsiya hisoblanadi.
Demak, bu PBS ichida sichqonchani harakatlantirish juda ko‘p reflow chaqirishini bildiradi. Eng yomoni shundaki, har bir reflow hisob-kitoblarni yakunlashi uchun brauzerni bir lahzaga to‘xtatib turadi. Ko‘proq reflow brauzerning ko‘proq to‘xtab qolishini anglatadi va foydalanish tajribasi yanada ko‘proq qotib ishlaydi (janky). Oddiyroq aytganda: juda ko‘p binafsharang sohalar = yomon.
Ma’lum bo‘lishicha, Ann haq bo‘lib chiqdi: bizda haqiqatan ham katta unumdorlik muammosi bor edi, shuning uchun jamoamiz darhol buni tuzatishga kirishdi.
Reflow / qotish muammosini hal qilish
Hamma narsadan oldin, ushbu reflow’ga nima sabab bo‘layotganini aniqlash uchun tadqiqot o‘tkazishimiz kerak edi. Aniqlanishicha, JavaScript’dagi ko‘plab oddiy amallar brauzerda reflow keltirib chiqarishi mumkin ekan.
DOM elementining kengligi, balandligi yoki joylashuvini olish uchun foydalaniladigan deyarli har qanday funksiya reflow’ni yuzaga keltirishi mumkin. Pol Ayrish (Paul Irish) buni keltirib chiqarishi mumkin bo‘lgan JavaScript xususiyatlari va funksiyalarining keng ro‘yxatini tuzgan. Yomon xabar shundaki, Page Builder Sandwich ushbu xususiyat va funksiyalarning ko‘pini ishlatadi va ularning barchasi reflow’ga sabab bo‘ladi:
- elem.getBoundingClientRect()
- window.getComputedStyle()
- window.scrollY
- va yana ko‘plab boshqalar…
1-yechim: Qimmatga tushuvchi reflow jarayonlarini keshlashtirish
Eng yaqqol yechim – ushbu xususiyatlar va funksiyalardan FOYDALANMASLIKDIR. Biroq, PBS ularga juda tayanadi va biz ularsiz ish ko‘ra olmaymiz.
Bizga qolgan yagona chora – ushbu funksiyalarga murojaatlarni iloji boricha qisqartirish edi. Bu yerdagi qiyin tomoni shundaki, biz ushbu xususiyat va funksiyalardan PBS’ning turli joylarida ko‘p marta va aksariyat hollarda bir xil DOM elementining o‘zida bir necha bor foydalangan edik. Buni hal qilish uchun har bir muammoli xususiyat/funksiya uchun o‘z keshlashtirish mexanizmimizni yaratdik.
Masalan, getBoundingClientRect funksiyasi bizga DOM elementining joriy top, left, width, height, right va bottom piksel o‘lchamlarini beradi. Biz PBS’da element konturlarini qayerda chizish kerakligini hisoblash uchun bu funksiyadan doimiy ravishda foydalanamiz. Mana, buning uchun yaratgan keshlashtirish funksiyamiz:
// Cache: Our client rects are kept here, we can use this to clear them later.
var elemsWithBoundingRects = [];
window.pbsGetBoundingClientRect = function( element ) {
// Check if we already got the client rect before.
if ( ! element._boundingClientRect ) {
// If not, get it then store it for future use.
element._boundingClientRect = element.getBoundingClientRect();
elemsWithBoundingRects.push( element );
}
return element._boundingClientRect;
};
getBoundingClientRect chaqirish o‘rniga endi pbsGetBoundingClientRect dan foydalandik. Endi har safar client rect so‘ralganda, bu qiymatni kelgusida murojaat qilish uchun saqlab qo‘yamiz va shu tariqa reflow chaqiruvlari sonini cheklaymiz.
Shuningdek, saqlangan client rect’lar eskirganda keshni tozalash usuli ham kerak bo‘ldi. Foydalanuvchi sahifani aylantirganda yoki ekranda elementlarni qo‘shganda/o‘zgartirganda/o‘chirganda saqlangan client rect qiymatlari o‘zgaradi. Shuning uchun bizga yangi client rect qiymatlari kerak bo‘lganda chaqirishimiz mumkin bo‘lgan keshni tozalash funksiyasini yaratdik:
var clearClientRects = function() {
var i;
for ( i = 0; i < elemsWithBoundingRects.length; i++ ) {
if ( elemsWithBoundingRects[ i ] ) {
elemsWithBoundingRects[ i ]._boundingClientRect = null;
}
}
elemsWithBoundingRects = [];
};
Biz odatda foydalanadigan boshqa qimmatga tushuvchi funksiya chaqiruvlari uchun ham xuddi shunday keshlashtirish mexanizmini qo‘lladik.
2-yechim: FastDom yordamida o‘qish va yozish amallarini birgalikda guruhlash
1-yechimni joriy qilish PBS unumdorligini oshirdi, ammo bu yetarli emas edi.
Google’dan biroz ko‘proq qidirib, Uilson Peyjning (Wilson Page) «‘layout thrashing’ning oldini olish» haqidagi maqolasiga duch keldik. Uilson shunday deydi:
Layout Thrashing (reflow) JavaScript DOM’ga ketma-ket bir necha marta shiddat bilan yozib, so‘ng undan o‘qiganda yuzaga keladi va bu hujjatning qayta hisoblanishiga (reflow) sabab bo‘ladi.
Ko‘rinib turibdiki, faqat reflow chaqiruvchi ma’lum xususiyat va funksiyalarga murojaatlarni cheklashning o‘zi yetarli emas. DOM ustida ketma-ket bir nechta «o‘qish» va «yozish» amallarini bajarganingizda, bu ham brauzer reflow’ini keltirib chiqaradi. Mana, to‘g‘ridan-to‘g‘ri Uilsonning saytidan olingan odatiy holat:
// Read
var h1 = element1.clientHeight;
// Write (invalidates layout)
element1.style.height = (h1 * 2) + 'px';
// Read (triggers reflow)
var h2 = element2.clientHeight;
// Write (invalidates layout)
element2.style.height = (h2 * 2) + 'px';
// Read (triggers reflow)
var h3 = element3.clientHeight;
// Write (invalidates layout)
element3.style.height = (h3 * 2) + 'px';
// Document reflows at end of frame
Yuqoridagi misol 3 ta reflow chaqiradi, chunki yozish amaldan so‘ng butun sahifaning o‘lchamlari eskiradi. Buning uchun taklif qilingan yechim – butun jarayon faqat 1 ta brauzer reflow’ini chaqirishi uchun barcha o‘qish va yozish amallarini bitta joyga to‘plash tarzida kodni qayta tartiblashdir:
// Read
var h1 = element1.clientHeight;
var h2 = element2.clientHeight;
var h3 = element3.clientHeight;
// Write (invalidates layout)
element1.style.height = (h1 * 2) + 'px';
element2.style.height = (h2 * 2) + 'px';
element3.style.height = (h3 * 2) + 'px';
// Document reflows at end of frame
Yuqoridagi kod parchasi muammoni hal qilishni oson qilib ko‘rsatadi. Ammo aslida, PBS’da reflow keltirib chiqaruvchi narsalarni o‘qish va yozish PBS hayotiy sikli davomida ko‘plab turli joylarda va turli vaqtlarda sodir bo‘ladi. Minglab qatorli JavaScript kodini qayta tashkil qilish imkonsiz edi.
Baxtimizga, Uilson o‘z maqolasida yechim ham taqdim etgan. U o‘qish va yozish amallarini bir joyga to‘plashga yordam beradigan FastDom nomli kichik utilita skriptini yaratgan.
Mana, Page Builder Sandwich’dan FastDom’dan foydalanadigan va yuqoridagi 1-yechimimizni ham o‘z ichiga olgan kod namunasi:
// Bunch together the reads from other threads.
fastdom.measure( function() {
// Use our cached client rect.
var rect = window.pbsGetBoundingClientRect( elem );
// Bunch together the writes from other threads.
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 ) );
FastDom nima qiladi: u barcha fastdom.measure chaqiruvlarini va barcha fastdom.mutate chaqiruvlarini birgalikda guruhlaydi va ularni asinxron tarzda bajaradi, shu tariqa brauzerdagi barcha reflow’larni kamaytirish uchun barcha o‘qish va yozishlarni samarali tarzda jamlaydi.
3-yechim: Style obyektlariga murojaat qilish qimmatga tushadi
Shuningdek, jsPerf yordamida uslublarni oluvchi ba’zi funksiyalar boshqalariga qaraganda tezroq ekanligini aniqladik.
PBS’da odatda elementning ma’lum bir uslubining joriy qiymatini olishimiz kerak bo‘ladi. Masalan, odatda yuqori oraliqni olish uchun bundan foydalanamiz:
var styles = window.getComputedStyle( elem );
var marginTop = styles.marginTop;
Biroq, buni quyidagicha qilish ancha tezroq ekanini aniqladik:
var marginTop = elem.style.marginTop;
Biroq, elem.style.marginTop va getComputedStyle juda farq qiladi. Birinchisi bizga faqat elementning inline uslubini beradi va hech qanday uslublar jadvalini hisobga olmaydi; ikkinchisi esa ham inline uslublarni, ham uslublar jadvallarini hisobga oladi.
Qolaversa, hatto getComputedStyle natijasi keshlashtirilgan bo‘lsa ham, u baribir juda sekin edi. Ma’lum bo‘lishicha, style obyektining o‘ziga murojaat qilish sekin jarayon ekan.
Baxtimizga, PBS elementlarga inline uslublarni qo‘shadi va inline uslublar (ko‘pincha) uslublar jadvalidan ustunroq bo‘lganligi sababli, faqat inline uslub qiymatini olish yetarli bo‘ladigan holatlar bo‘ladi. Shunday qilib, agar yuqoridagi ikkala usulni birlashtirsak, ancha tez ishlaydigan kodga ega bo‘lamiz:
var marginTop = elem.style.marginTop;
if ( ! marginTop ) {
var styles = window.getComputedStyle( elem );
marginTop = styles.marginTop;
}
Kodni takomillashtirish natijalari
Yuqoridagi 3 ta yechimimiz katta izlanish, sinov va amaliy joriy qilishni talab qildi (hamda Shriftlar funksiyamizning chiqarilishini kechiktirdi), ammo oxir-oqibat bularning barchasi o‘zini oqladi.
Sichqonchani ekranda 2 soniya davomida harakatlantirish bo‘yicha dastlabki sinovimizga qaytadigan bo‘lsak. Mana eski xunuk/qotib ishlaydigan vaqt jadvali:
Mana esa unumdorlik yaxshilangandan so‘ng yangi vaqt jadvalimiz qanday ko‘rinishga ega bo‘lgani:
Ko‘rib turganingizdek, grafikda binafsharang joylar ancha kam. Brauzer tomonidan bajarilayotgan jarayonlar hajmida ham sezilarli farq bor. Pastdagi to‘g‘ri to‘rtburchaklar ham ingichkaroq, ya’ni jarayonlar avvalgiga qaraganda tezroq bajarilmoqda.
Agar eski va yangi grafiklardagi baland/keng binafsharang sohalar miqdorini taqqoslasangiz, biz naq 200% unumdorlik o‘sishiga guvoh bo‘lamiz! Shuningdek, barcha yo‘nalishlarda unumdorlik oshganini ko‘rmoqdamiz. Umuman yomon emas!
Xulosalar va olingan saboqlar
Rostini aytsak, Ann bu masalani ko‘tarmaguncha PBS’da unumdorlik muammolari bor deb o‘ylamagan edik. Asosan shuning uchunki, biz dasturlashga mo‘ljallangan kompyuterlardan foydalanganmiz va unumdorlik to‘siqlari unchalik sezilmagan. O‘tmishga nazar tashlab shuni aytish mumkinki, unumdorlikni tekshirish eng asosiy ustuvor vazifalardan biri bo‘lishi kerak edi. Ammo hech bo‘lmaganda hozir, 4.3 versiyasidan boshlab bu hal qilindi. 
O‘zining WordPress plaginining unumdorligini yaxshilamoqchi bo‘lganlar, ayniqsa siz ham ko‘p miqdorda JavaScript ishlatayotgan bo‘lsangiz, ushbu tajribadan olingan xulosalarim quyidagicha:
- Reflow keltirib chiqaradigan xususiyatlar/funksiyalarga chaqiruvlarni cheklang/keshlang. Qo‘llanma sifatida bundan foydalaning: What forces layout/reflow. The comprehensive list.
- DOM uslublariga «o‘qish» va «yozish» amallarini birgalikda guruhlang. Buni siz uchun bajarishi uchun FastDom‘dan foydalaning.
- Chrome’ning Timeline profayleridan foydalaning va reflow’lar (binafsharang sohalar) iloji boricha kam bo‘lishiga ishonch hosil qiling.