Si e rritëm performancën e shtojcës sonë me 200%

Më 2 shkurt 2017, Ann Taylor shkroi artikullin 12 gjëra që duhet të shohim nga ndërtuesit e faqeve të WordPress në 2017 + Kush e ka bërë tashmë siç duhet në CodeInWP. Ne e kemi përmirësuar vazhdimisht Page Builder Sandwich për më shumë se një vit: bëjmë kërkimet tona, dëgjojmë klientët tanë, kështu që menduam se Page Builder Sandwich do të futej në artikullin e CodeInWP “Kush e bëri siç duhet”. Megjithatë, nuk u futëm, për shkak të performancës. ?

Kështu që e pyetëm Ann-ën për këtë, dhe ajo u përgjigj:

…E provova pak versionin falas. Tregon shumë premisë: shumë module, ndërfaqe mjaft intuitive, përvojë e shkëlqyer WYSIWYG, gjithçka zhvendoset lehtësisht. Megjithatë, kur kisha rreth 10 module në faqe, u bë e vështirë të vazhdoja punën me të për shkak të shpejtësisë së ulët të redaktimit…

Pra, sipas saj, Page Builder Sandwich ka probleme me performancën. Dhe meqë ne gjithmonë i dëgjojmë reagimet, e shqyrtuam atë.

Testimi i Performancës

Na duhej një mënyrë për të kontrolluar performancën e PBS-së; meqenëse PBS kryesisht funksionon në shfletues nëpërmjet Javascript, profiluesi Timeline i Chrome ishte mjeti i duhur për këtë punë. Profiluesi i linjës kohore mund të identifikojë dhe matë se cilat procese po ndodhin në një faqe interneti gjatë kohës së ekzekutimit, gjë që është e përsosur për gjetjen e ngushticave të shpejtësisë.

Menduam se duhet të testonim fillimisht hapat më të thjeshtë – lëvizjen e miut nëpër disa elemente dhe rreshta. E thjeshtë apo jo? Ishim të bindur se nuk do të hasim probleme me performancën me diçka kaq të thjeshtë. Fatkeqësisht, e kishim gabim.

Pasi shtypëm butonin e regjistrimit, lëvizëm miun për rreth 2 sekonda në ekran, pastaj shtypëm butonin e ndalimit. Ja linja kohore rezultuese nga profiluesi:

Dhe ja ku doli. Është plot e përplot me ngushtica të performancës!

Ka shumë gjëra që ndodhin në grafik, por problemi më i dukshëm është se ka shumë zona ngjyrë vjollce në të (gjë që nuk duhet të ndodhë). Përveç kësaj, këto zona ngjyrë vjollce janë dukshëm shumë të gjera dhe të gjitha duket se arrijnë majën e grafikut. Gjithashtu do të vini re se grafiku është gjithmonë plot gjithashtu, dhe se zonat ngjyrë vjollce pothuajse kurrë nuk zbresin në zero. Duket sikur shfletuesi është gjithmonë i zënë duke bërë diçka dhe pothuajse kurrë nuk pushon.

Por çfarë nënkuptojnë në të vërtetë të gjitha ato zona ngjyrë vjollce?

Profiluesi ishte mjaft i sjellshëm për të na treguar se ato quhen reflows. Ja përkufizimi i asaj se çfarë është një reflow nga faqja e Google Developers:

Reflow është emri i procesit të shfletuesit të uebit për rillogaritjen e pozicioneve dhe gjeometrive të elementeve në dokument.. reflow është një operacion që bllokon përdoruesin në shfletues.

Pra, kjo do të thotë se lëvizja e miut në PBS shkakton një morí me reflows. Çfarë është edhe më keq është se çdo reflow e pezullon shfletuesin përkohësisht në mënyrë që të përfundojë llogaritjet e tij. Më shumë reflows nënkuptojnë më shumë ndalesa të shfletuesit, dhe përvoja bëhet edhe më e ngadaltë dhe me ngecje. Me terma më të thjeshtë, shumë zona ngjyrë vjollce = keq.

Kështu që rezultoi se Ann kishte të drejtë: ne kemi vërtet një ngushticë të madhe të performancës, ndaj ekipi ynë iu fut menjëherë punës për ta rregulluar menjëherë.

Rregullimi i Problemit Tonë me Reflow / Ngecjet

Para çdo gjëje tjetër, na duhej të bënim disa kërkime për të zbuluar se çfarë e shkakton këtë reflow në radhë të parë. Zbuluam se me sa duket, shumë gjëra të zakonshme në Javascript mund ta provokojnë shfletuesin të shkaktojë një reflow.

Pothuajse çdo funksion që mund të përdorni për të marrë gjerësinë, lartësinë ose pozicionin e një elementi DOM mund të shkaktojë një reflow. Paul Irish shkroi një listë të gjerë të të gjitha vetive dhe funksioneve në Javascript që mund ta bëjnë këtë. Lajmi i keq është se Page Builder Sandwich përdor shumë nga këto veti dhe funksione, të cilat të gjitha shkaktojnë reflow:

  • elem.getBoundingClientRect()
  • window.getComputedStyle()
  • window.scrollY
  • dhe shumë të tjera…

Rregullimi #1: Ruajtja në Memorie e Proceseve të Kushtueshme të Reflow-ut

Zgjidhja më e dukshme është të MOS përdoren ato veti dhe funksione. Megjithatë, PBS mbështetet shumë tek to dhe nuk mund të bëjmë dot pa to.

Zgjidhja që na mbetet është të reduktojmë sa më shumë thirrjet ndaj këtyre funksioneve. Pjesa e vështirë këtu ishte se ne i përdornim ato veti dhe funksione shumë herë në pjesë të ndryshme të PBS-së, dhe shumicën e kohës, i përdornim ato në të njëjtin element DOM disa herë. Për ta rregulluar këtë, ne krijuam mekanizmin tonë të ruajtjes në memorie (caching) për çdo veti/funksion problematik.

Për shembull, funksioni getBoundingClientRect na jep matjet aktuale në pikselë për sipër, majtas, gjerësi, lartësi, djathtas dhe poshtë të një elementi DOM. Ne e përdorim këtë funksion gjithë kohës për të llogaritur vendndodhjen se ku duhet të vizatohen konturet e elementit në PBS. Ja funksioni i ruajtjes në memorie që krijuam për këtë:

// Cache: Drejtkëndëshat tanë të klientit mbahen këtu, mund ta përdorim këtë për t'i pastruar më vonë.
var elemsWithBoundingRects = [];

window.pbsGetBoundingClientRect = function( element ) {

	// Kontrollo nëse e kemi marrë tashmë drejtkëndëshin e klientit më parë.
	if ( ! element._boundingClientRect ) {

		// Nëse jo, merre dhe ruaje për përdorim në të ardhmen.
		element._boundingClientRect = element.getBoundingClientRect();
		elemsWithBoundingRects.push( element );
	}
	return element._boundingClientRect;
};
​

Në vend që të thërrisnim getBoundingClientRect , tani përdorëm pbsGetBoundingClientRect . Tani sa herë që kërkohet client rect, ne e ruajmë atë vlerë për referencë në të ardhmen, duke kufizuar kështu numrin e shkaktuesve të reflow-ut.

Gjithashtu na duhej një mënyrë për të pastruar memorien e përkohshme kur drejtkëndëshat e ruajtur të klientit bëhen të pavlefshëm. Vlerat e client rect-it të ruajtur ndryshojnë kur përdoruesi lëviz faqen ose shton/ndryshon/heq elemente në ekran. Kështu që ne krijuam një funksion pastrimi të memories së përkohshme që mund ta thërrasim sa herë që na nevojiten vlera të freskëta të client rect:

var clearClientRects = function() {
	var i;
	for ( i = 0; i < elemsWithBoundingRects.length; i++ ) {
		if ( elemsWithBoundingRects[ i ] ) {
			elemsWithBoundingRects[ i ]._boundingClientRect = null;
		}
	}
	elemsWithBoundingRects = [];
};​

Ne aplikuam gjithashtu të njëjtin mekanizëm ruajtjeje në memorie për thirrje të tjera funksionesh të kushtueshme që zakonisht përdorim.

Rregullimi #2: Grupimi së Bashku i Leximeve dhe Shkrimeve Duke Përdorur FastDom

Zbatimi i rregullimit #1 rriti performancën e PBS-së, por nuk mjaftonte.

Duke kërkuar pak më shumë në Google, hasëm në një artikull nga Wilson Page mbi Parandalimin e ‘layout thrashing’. Wilson thotë:

Layout Thrashing (reflow) ndodh kur JavaScript shkruan dhe më pas lexon në mënyrë të përsëritur dhe të dhunshme nga DOM, duke shkaktuar reflow të dokumentit disa herë.

Siç duket, nuk mjafton të kufizoni thirrjet tuaja drejt vetive dhe funksioneve specifike që shkaktojnë reflow. Kur kryeni “lexime” dhe “shkrime” të njëpasnjëshme në DOM, kjo gjithashtu shkakton një reflow në shfletues. Ja një skenar tipik, marrë drejtpërdrejt nga faqja e Wilson-it:

// Lexim
var h1 = element1.clientHeight;

// Shkrim (zhvlerëson faqosjen)
element1.style.height = (h1 * 2) + 'px';

// Lexim (shkakton reflow)
var h2 = element2.clientHeight;

// Shkrim (zhvlerëson faqosjen)
element2.style.height = (h2 * 2) + 'px';

// Lexim (shkakton reflow)
var h3 = element3.clientHeight;

// Shkrim (zhvlerëson faqosjen)
element3.style.height = (h3 * 2) + 'px';

// Dokumenti bën reflow në fund të frame-it​

Shembulli i mësipërm shkakton 3 reflows sepse përmasat e të gjithë faqes zhvlerësohen pas një shkrimi. Rregullimi i sugjeruar për këtë është të rirenditet kodi për t’i grupuar të gjitha leximet dhe shkrimet së bashku në mënyrë që e gjithë kjo të shkaktojë vetëm 1 reflow në shfletues:

// Lexim
var h1 = element1.clientHeight;
var h2 = element2.clientHeight;
var h3 = element3.clientHeight;

// Shkrim (zhvlerëson faqosjen)
element1.style.height = (h1 * 2) + 'px';
element2.style.height = (h2 * 2) + 'px';
element3.style.height = (h3 * 2) + 'px';

// Dokumenti bën reflow në fund të frame-it​

Pjesa e kodit më sipër e bën rregullimin e problemit të duket i lehtë. Por në realitet, në PBS, leximi dhe shkrimi i elementeve që shkaktojnë reflow ndodh në shumë vende të ndryshme dhe në shumë kohë të ndryshme gjatë ciklit të jetës së PBS-së. Riorganizimi i mijëra rreshtave të kodit Javascript nuk është i realizueshëm.

Për fatin tonë të mirë, Wilson ofroi gjithashtu një zgjidhje në artikullin e tij. Ai krijoi një skript të vogël ndihmës për të ndihmuar me grupimin e këtyre leximeve dhe shkrimeve së bashku, të quajtur FastDom.

Ja disa rreshta kodi nga Page Builder Sandwich që përdorin FastDom dhe gjithashtu përfshijnë Rregullimin #1 më lart:

// Bashko leximet nga fijet e tjera.
fastdom.measure( function() {

	// Përdor drejtkëndëshin tonë të ruajtur në memorie.
	var rect = window.pbsGetBoundingClientRect( elem );

	// Bashko shkrimet nga fijet e tjera.
	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 ) );​

Ajo që bën FastDom është se grupon së bashku të gjitha thirrjet fastdom.measure dhe të gjitha thirrjet fastdom.mutate dhe i ekzekuton ato në mënyrë asinkrone, duke grupuar efektivisht të gjitha leximet dhe shkrimet së bashku për të pakësuar të gjitha reflow-t në shfletues.

Rregullimi #3: Qasja në Objektet e Stilit Është e Kushtueshme

Zbuluam gjithashtu me ndihmën e jsPerf se disa funksione për marrjen e stileve ishin më të shpejta se të tjerat.

Në PBS, zakonisht na duhet të marrim vlerën aktuale të një stili specifik të një elementi. Për shembull, normalisht përdorim këtë për të marrë margin-in e sipërm:

var styles = window.getComputedStyle( elem );
var marginTop = styles.marginTop;​

Por, zbuluam se kryerja e kësaj është shumë më e shpejtë:

var marginTop = elem.style.marginTop;​

Megjithatë, elem.style.marginTop dhe getComputedStyle janë shumë të ndryshme. E para na jep vetëm stilizimin inline të elementit dhe nuk merr parasysh asnjë fletë stili; kurse e dyta merr parasysh si stilet inline, ashtu edhe fletët e stilit.

Për më tepër, edhe kur rezultati i getComputedStyle ruhej në memorie, ishte ende vërtet i ngadaltë. Siç rezulton, vetë qasja në objektin e stilit është procesi i ngadaltë.

Për fatin tonë të mirë, PBS u shton stile inline elementeve, dhe meqë stilet inline kanë përparësi (shumicën e kohës) ndaj fletëve të stilit, do të ketë raste kur marrja e vlerës së stilit inline do të mjaftonte. Kështu që nëse kombinojmë dy metodat e mësipërme, mund të arrijmë në një kod që ekzekutohet shumë më shpejt:

var marginTop = elem.style.marginTop;
if ( ! marginTop ) {
	var styles = window.getComputedStyle( elem );
	marginTop = styles.marginTop;
}​

Rezultatet e Përmirësimeve të Kodit

Tre rregullimet tona më lart kërkuan shumë kërkime, testim dhe zbatim aktual (dhe vonuan lëshimin e veçorisë sonë të Fonteve), por në fund ia vlejti plotësisht.

Duke u kthyer te testi ynë fillestar i lëvizjes së miut nëpër ekran për 2 sekonda. Ja linja kohore e vjetër e ngadaltë dhe me ngecje:

Dhe ja si duket linja jonë e re kohore pas përmirësimeve të performancës:

Siç mund ta shihni, ka më pak elemente ngjyrë vjollce në grafik. Dhe ka një ndryshim të dukshëm në sasinë e proceseve që po kryhen nga shfletuesi. Pjesët drejtkëndore në pjesën e poshtme janë gjithashtu më të holla, gjë që do të thotë se proceset po ekzekutohen më shpejt se më parë.

Nëse krahasoni sasinë e zonave të larta/të gjera ngjyrë vjollce në grafikët e vjetër dhe të rinj, po shohim një rritje marramendëse të performancës prej 200%! Po shohim gjithashtu rritje të performancës kudo. Aspak keq!

Përfundimet dhe Mësimet e Nxjerra

Të themi të drejtën, nuk mendonim se PBS kishte probleme me performancën përpara se ta përmendte Ann. Kryesisht sepse po përdornim kompjuterë të fokusuar në zhvillim dhe ngushticat e performancës nuk ishin aq të dukshme. Duke parë prapa, kontrolli i performancës duhet të kishte qenë një nga prioritetet kryesore. Por të paktën tani, duke filluar nga versioni 4.3, kjo është zgjidhur. ?

Për ata që dëshirojnë të përmirësojnë performancën e shtojcës së tyre të WordPress, veçanërisht nëse përdorni gjithashtu shumë Javascript, ja përfundimet e mia nga kjo përvojë:

  1. Kufizoni/ruani në memorie thirrjet tuaja drejt vetive/funksioneve që shkaktojnë reflow. Përdoreni këtë si udhëzues: Çfarë e detyron layout/reflow. Lista gjithëpërfshirëse.
  2. Grupini së bashku “leximet” dhe “shkrimet” në stilet e DOM-it. Përdorni FastDom për ta bërë këtë për ju.
  3. Përdorni profiluesin Timeline të Chrome dhe sigurohuni që të keni sa më pak reflows (elemente ngjyrë vjollce) të jetë e mundur.