2017 жылғы 2 ақпанда Энн Тейлор CodeInWP сайтында 12 Things We Need to See From WordPress Page Builders in 2017 + Who Already Gets It Right атты мақала жазды. Біз Page Builder Sandwich-ті бір жылдан астам уақыт бойы үздіксіз жетілдіріп келеміз: зерттеулер жүргіземіз, тұтынушыларымыздың пікірін тыңдаймыз, сондықтан Page Builder Sandwich CodeInWP-тің «Кімнің қолынан келді» деген мақаласына кіреді деп ойлаған едік. Алайда, өнімділікке байланысты олай болмады.
Сондықтан біз Энннен бұл туралы сұрадық, ол былай деп жауап берді:
…Мен тегін нұсқасын біраз сынап көрдім. Үміт күттірерлік дүниелер көп: модульдер көп, интерфейсі өте түсінікті, тамаша WYSIWYG тәжірибесі, барлығы оңай сүйреледі. Дегенмен, бетте шамамен 10 модуль болған кезде, өңдеу жылдамдығының төмендігіне байланысты онымен жұмысты жалғастыру қиынға соқты…
Демек, оның айтуынша, Page Builder Sandwich-те өнімділік мәселелері бар. Ал біз кері байланысқа әрқашан құлақ асатындықтан, мұны тексеріп көрдік.
Өнімділікті тестілеу
Бізге PBS өнімділігін тексеретін тәсіл қажет болды; PBS көбінесе Javascript арқылы браузерде жұмыс істейтіндіктен, Chrome Timeline профайлері осы жұмыс үшін ең қолайлы құрал болды. Timeline профайлері орындалу барысында веб-бетте қандай процестер жүріп жатқанын анықтап, өлшей алады, бұл жылдамдықты тежейтін тұстарды табуға өте ыңғайлы.
Біз алдымен ең қарапайым қадамдарды сынап көруді жөн көрдік – курсорды бірнеше элемент пен қатардың үстінен жүргізу. Қарапайым, солай ма? Осындай қарапайым нәрседе өнімділік мәселесі туындамайтынына сенімді едік. Өкінішке қарай, қателесіппіз.
Жазба түймесін басқан соң, тінтуірді экранда шамамен 2 секунд жүргізіп, содан кейін тоқтату түймесін бастық. Профайлерден шыққан timeline нәтижесі міне:
Міне, қызық! Ол өнімділіктің кедергілеріне лық толы!
Графикте көптеген әрекеттер орын алуда, бірақ ең айқын мәселе — ондағы күлгін аймақтардың көптігі (негізі олай болмауы керек). Оның үстіне, бұл күлгін аймақтар көзбен қарағанда өте кең және барлығы дерлік графиктің ең жоғарғы шыңына жетіп тұр. Сондай-ақ графиктің әрқашан толық екенін және күлгін аймақтардың нөлге дерлік түспейтінін байқайсыз. Браузер әрдайым бірдеңемен бос болмай, еш демалмайтындай әсер қалдырады.
Бірақ осы күлгін аймақтардың барлығы нені білдіреді?
Профайлер олардың reflow (қайта орналастыру) деп аталатынын көрсетті. Google Developer сайтынан:
Reflow — бұл құжаттағы элементтердің орындары мен геометриясын қайта есептеуге арналған веб-браузер процесінің атауы.. reflow браузерде пайдаланушы әрекетін бұғаттайтын операция болып табылады.
Демек, PBS ішінде тінтуірді қозғалту өте көп reflow тудырады. Ең сорақысы, әрбір reflow есептеулерді аяқтау үшін браузерді бір сәтке кідіртеді. Көбірек reflow браузердің көбірек кідіруін білдіреді, ал пайдалану тәжірибесі бұдан да бетер баяулап, іркіледі. Қарапайым сөзбен айтқанда, күлгін аймақтың көп болуы = нашар.
Сөйтсек, Энннің айтқаны дұрыс болып шықты: бізде расында да өнімділіктің үлкен мәселесі бар екен, сондықтан командамыз оны дереу түзетуге кірісті.
Reflow / іркіліс мәселесін шешу
Ең алдымен, осы reflow-ға нақты не себеп болатынын анықтау үшін біраз зерттеу жүргізу керек болды. Біз Javascript-тегі көптеген қарапайым әрекеттер браузерде reflow тудыруы мүмкін екенін анықтадық.
DOM элементінің енін, биіктігін немесе орнын алу үшін пайдаланылатын кез келген дерлік функция reflow тудыруы мүмкін. Пол Айриш мұны тудыратын Javascript-тегі барлық қасиеттер мен функциялардың толық тізімін жазған болатын. Жағымсыз жаңалық — Page Builder Sandwich осы қасиеттер мен функциялардың көбін пайдаланады және олардың барлығы reflow тудырады:
- elem.getBoundingClientRect()
- window.getComputedStyle()
- window.scrollY
- және тағы басқалар…
№1 түзету: Көп ресурс талап ететін Reflow процестерін кэштеу
Ең айқын шешім — бұл қасиеттер мен функцияларды пайдаланбау. Алайда, PBS оларға қатты тәуелді және біз оларсыз жұмыс істей алмаймыз.
Бізге қалған жалғыз шешім — осы функцияларды шақыруды барынша азайту болды. Мұндағы қиындық — бұл қасиеттер мен функцияларды PBS-тің әртүрлі жерлерінде жиі қолданғандығымызда және көп жағдайда оларды бір DOM элементіне бірнеше рет қолданғандығымызда болды. Мұны түзету үшін біз әрбір проблемалық қасиет/функция үшін өзіміздің кэштеу механизмімізді жасадық.
Мысалы, getBoundingClientRect функциясы бізге DOM элементінің пикселдегі ағымдағы жоғарғы, сол жақ, ені, биіктігі, оң жақ және төменгі өлшемдерін береді. Біз бұл функцияны PBS ішінде элемент жиектерін қай жерде сызу керектігін есептеу үшін үнемі қолданамыз. Міне, осы үшін біз жасаған кэштеу функциясы:
// 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 шақырудың орнына біз енді pbsGetBoundingClientRect қолдандық. Енді client rect сұралған сайын, бұл мәнді болашақта пайдалану үшін сақтап отырамыз, осылайша reflow тудыру жиілігін шектейміз.
Сондай-ақ сақталған client rect-тердің жарамдылығы жойылған кезде кэшті тазалау тәсілі қажет болды. Пайдаланушы бетті айналдырғанда немесе экранда элементтерді қосқанда/өзгерткенде/жойғанда сақталған client rect мәндері өзгереді. Сондықтан жаңа client rect мәндері қажет болған кезде шақыруға болатын кэшті тазалау функциясын жасадық:
var clearClientRects = function() {
var i;
for ( i = 0; i < elemsWithBoundingRects.length; i++ ) {
if ( elemsWithBoundingRects[ i ] ) {
elemsWithBoundingRects[ i ]._boundingClientRect = null;
}
}
elemsWithBoundingRects = [];
};
Біз әдетте қолданатын басқа да ресурсты көп қажет ететін функцияларды шақыру үшін осы кэштеу механизмін қолдандық.
№2 түзету: FastDom көмегімен оқу және жазу әрекеттерін топтастыру
№1 түзетуді енгізу PBS өнімділігін арттырды, бірақ бұл жеткіліксіз болды.
Google-ден біраз іздеп, біз Уилсон Пейджтің «layout thrashing»-тің алдын алу туралы мақаласын таптық. Уилсон былай дейді:
Layout Thrashing (reflow) JavaScript DOM-ға бірнеше рет қатарынан жазып, содан кейін оқыған кезде орын алып, құжатта reflow тудырады.
Шамасы, тек reflow тудыратын белгілі бір қасиеттер мен функцияларды шақыруды шектеу жеткіліксіз екен. DOM-да бірнеше рет қатарынан «оқу» және «жазу» операцияларын орындаған кезде де браузердің reflow процесі іске қосылады. Міне, тікелей Уилсонның сайтынан алынған типтік мысал:
// 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
Жоғарыдағы мысал 3 reflow тудырады, себебі әрбір жазу әрекетінен кейін бүкіл беттің өлшемдері жарамсыз болып қалады. Мұны шешу үшін кодты қайта реттеп, бүкіл әрекет браузерде бар болғаны 1 reflow тудыруы үшін барлық оқу мен жазуды топтастыру ұсынылады:
// 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
Жоғарыдағы үзінді мәселені шешу оңай сияқты етіп көрсетеді. Бірақ іс жүзінде, PBS-те reflow тудыратын әрекеттерді оқу мен жазу PBS өмірлік циклі барысында көптеген әртүрлі жерлерде және әртүрлі уақытта орын алады. Мыңдаған жолдан тұратын Javascript кодын қайта құрылымдау мүмкін емес еді.
Бағымызға қарай, Уилсон өз мақаласында шешімді де ұсынған. Ол осы оқу мен жазу әрекеттерін топтастыруға көмектесетін FastDom атты шағын утилита скриптін жасаған.
Міне, FastDom-ды қолданатын және жоғарыдағы №1 түзетуімізді қамтитын Page Builder Sandwich-тен алынған кейбір код:
// 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 барлық fastdom.measure шақыруларын және барлық fastdom.mutate шақыруларын біріктіріп, оларды асинхронды түрде орындайды, бұл браузердегі барлық reflow-ды азайту үшін оқу мен жазуды тиімді түрде топтастырады.
№3 түзету: Стиль нысандарына қол жеткізу көп ресурс алады
Сондай-ақ біз jsPerf көмегімен кейбір стиль алатын функциялардың басқаларына қарағанда жылдамырақ екенін анықтадық.
PBS-те әдетте элементтің белгілі бір стилінің ағымдағы мәнін алу қажет болады. Мысалы, жоғарғы шегіністі алу үшін әдетте мынаны қолданамыз:
var styles = window.getComputedStyle( elem ); var marginTop = styles.marginTop;
Бірақ мынадай тәсіл әлдеқайда жылдамырақ болатынын байқадық:
var marginTop = elem.style.marginTop;
Дегенмен, elem.style.marginTop және getComputedStyle бір-бірінен қатты ерекшеленеді. Біріншісі тек элементтің кірістірілген (inline) стилін береді және ешқандай стильдер кестесін ескермейді; ал екіншісі кірістірілген стильдерді де, стильдер кестесін де ескереді.
Оның үстіне, тіпті getComputedStyle нәтижесі кэштелген күннің өзінде де, ол өте баяу жұмыс істеді. Белгілі болғандай, стиль нысанының өзіне қол жеткізу баяу процесс болып табылады.
Бағымызға қарай, PBS элементтерге кірістірілген стильдерді тіркейді, ал кірістірілген стильдер стильдер кестесінен басым болатындықтан (көп жағдайда), тек кірістірілген стиль мәнін алу жеткілікті болатын жағдайлар болады. Сондықтан жоғарыдағы екі әдісті біріктірсек, әлдеқайда жылдам орындалатын код шығара аламыз:
var marginTop = elem.style.marginTop;
if ( ! marginTop ) {
var styles = window.getComputedStyle( elem );
marginTop = styles.marginTop;
}
Кодты жақсарту нәтижелері
Жоғарыдағы 3 түзетуіміз көптеген зерттеуді, тестілеуді және нақты іске асыруды талап етті (және біздің «Қаріптер» функциясының шығарылымын кешіктірді), бірақ соңында бәрі өзін ақтады.
Тінтуірді экран бойынша 2 секунд қозғалту туралы бастапқы тестімізге оралайық. Міне, ескі көріксіз/іркілмелі timeline:
Ал мынау — өнімділікті жақсартқаннан кейінгі жаңа timeline қандай болып көрінетіні:
Көріп отырғаныңыздай, графиктегі күлгін элементтер азайды. Сондай-ақ браузер орындайтын процестер көлемінде айтарлықтай айырмашылық бар. Төмендегі тікбұрышты элементтер де жұқарған, яғни процестер бұрынғыға қарағанда жылдамырақ орындалып жатыр.
Егер сіз ескі және жаңа графиктердегі биік/кең күлгін аймақтардың көлемін салыстырсаңыз, біз таңқаларлық өнімділіктің 200%-ға өскенін көреміз! Біз сондай-ақ барлық бағыттар бойынша өнімділіктің артқанын көріп отырмыз. Жаман емес!
Негізгі қорытындылар мен сабақтар
Шынымды айтсам, Энн айтқанға дейін біз PBS-те өнімділік мәселесі бар деп ойламағанбыз. Себебі біз негізінен әзірлеуге арналған қуатты құрылғыларды қолданғандықтан, өнімділік кедергілері соншалықты байқалмаған еді. Өткенге көз жүгіртсек, өнімділікті тексеру басты басымдықтардың бірі болуы керек еді. Бірақ кем дегенде қазір, 4.3 нұсқасынан бастап бұл мәселе шешілді.
Өздерінің WordPress плагиндерінің өнімділігін арттырғысы келетіндер үшін, әсіресе Javascript-ті көп қолданатын болсаңыз, осы тәжірибеден түйген түйіндерім мынадай:
- Reflow тудыратын қасиеттер мен функцияларды шақыруды шектеңіз/кэштеңіз. Мынаны нұсқаулық ретінде пайдаланыңыз: What forces layout/reflow. The comprehensive list.
- DOM стильдерін «оқу» және «жазу» операцияларын топтастырыңыз. Мұны орындау үшін FastDom қолданыңыз.
- Chrome Timeline профайлерін қолданыңыз және reflow (күлгін белгілер) барынша аз болуын қадағалаңыз.