Habr front-end ontwikkelaar logs: herfaktorering en reflektering

Habr front-end ontwikkelaar logs: herfaktorering en reflektering

Ek was nog altyd geïnteresseerd in hoe Habr van binne gestruktureer is, hoe die werkvloei gestruktureer is, hoe kommunikasie gestruktureer is, watter standaarde gebruik word en hoe kode oor die algemeen hier geskryf word. Gelukkig het ek so ’n geleentheid gekry, want ek het onlangs deel van die habra-span geword. Deur die voorbeeld van 'n klein herfaktorering van die mobiele weergawe te gebruik, sal ek probeer om die vraag te beantwoord: hoe is dit om hier aan die voorkant te werk. In die program: Node, Vue, Vuex en SSR met sous uit notas oor persoonlike ervaring in Habr.

Die eerste ding wat u van die ontwikkelingspan moet weet, is dat daar min van ons is. Nie genoeg nie - dit is drie voorspelers, twee agterspelers en die tegniese voorsprong van alle Habr - Baxley. Daar is natuurlik ook 'n toetser, 'n ontwerper, drie Vadim, 'n wonderbesem, 'n bemarkingspesialis en ander Bumburums. Maar daar is net ses direkte bydraers tot Habr se bronne. Dit is nogal skaars - 'n projek met 'n multimiljoen-dollar-gehoor, wat van buite na 'n reuse-onderneming lyk, lyk in werklikheid meer na 'n gesellige begin met die platste organisatoriese struktuur moontlik.

Soos baie ander IT-maatskappye, bely Habr Agile idees, CI-praktyke, en dit is al. Maar volgens my gevoelens ontwikkel Habr as 'n produk meer in golwe as voortdurend. Dus, vir verskeie naellope in 'n ry, kodeer ons ywerig iets, ontwerp en herontwerp, breek iets en maak dit reg, los kaartjies op en skep nuwes, trap 'n hark en skiet onsself in die voete, om uiteindelik die kenmerk vry te stel in produksie. En dan kom daar 'n sekere stilte, 'n tydperk van herontwikkeling, tyd om te doen wat in die "belangrik-nie dringend" kwadrant is.

Dit is juis hierdie “buiteseisoen” naelloop wat hieronder bespreek sal word. Hierdie keer het dit 'n herfaktorering van die mobiele weergawe van Habr ingesluit. Oor die algemeen het die maatskappy groot verwagtinge daarvoor, en in die toekoms behoort dit die hele dieretuin van Habr se inkarnasies te vervang en 'n universele kruisplatform-oplossing te word. Eendag sal daar aanpasbare uitleg, PWA, vanlynmodus, gebruikersaanpassing en baie ander interessante dinge wees.

Kom ons stel die taak op

Een keer, by 'n gewone stand-up, het een van die voorkant gepraat oor probleme in die argitektuur van die kommentaar-komponent van die mobiele weergawe. Met dit in gedagte het ons 'n mikro-byeenkoms in die formaat van groeppsigoterapie gereël. Almal het om die beurt gesê waar dit seer is, hulle het alles op papier aangeteken, hulle het simpatie gehad, hulle het verstaan, behalwe dat niemand hande geklap het nie. Die resultaat was 'n lys van 20 probleme, wat dit duidelik gemaak het dat mobiele Habr nog 'n lang en netelige pad na sukses het.

Ek was hoofsaaklik bekommerd oor die doeltreffendheid van hulpbrongebruik en wat 'n gladde koppelvlak genoem word. Elke dag, op die huis-werk-huis-roete, het ek gesien hoe my ou foon desperaat probeer om 20 opskrifte in die voer te vertoon. Dit het so iets gelyk:

Habr front-end ontwikkelaar logs: herfaktorering en reflekteringMobiele Habr-koppelvlak voor herfaktorering

Wat gaan hier aan? Kortom, die bediener het die HTML-bladsy op dieselfde manier aan almal bedien, ongeag of die gebruiker aangemeld was of nie. Dan word die kliënt JS gelaai en versoek weer die nodige data, maar aangepas vir magtiging. Dit wil sê, ons het eintlik twee keer dieselfde werk gedoen. Die koppelvlak het geflikker, en die gebruiker het 'n goeie honderd ekstra kilogrepe afgelaai. In detail het alles nog meer grillerig gelyk.

Habr front-end ontwikkelaar logs: herfaktorering en reflekteringOu SSR-CSR-skema. Magtiging is slegs moontlik in stadiums C3 en C4, wanneer Node JS nie besig is om HTML te genereer nie en kan instaanversoeke na die API.

Ons argitektuur van daardie tyd is baie akkuraat beskryf deur een van die Habr-gebruikers:

Die mobiele weergawe is kak. Ek vertel dit soos dit is. 'n Verskriklike kombinasie van SSR en CSR.

Ons moes dit erken, maak nie saak hoe hartseer dit was nie.

Ek het die opsies beoordeel, 'n kaartjie in Jira geskep met 'n beskrywing op die vlak van "dis nou sleg, doen dit reg" en die taak in breë trekke ontbind:

  • hergebruik data,
  • verminder die aantal hertrekkings,
  • elimineer duplikaatversoeke,
  • maak die laaiproses duideliker.

Kom ons hergebruik die data

In teorie is bedienerkant-weergawe ontwerp om twee probleme op te los: om nie te ly aan soekenjinbeperkings in terme van SPA-indeksering en verbeter die metrieke FMP (vererger onvermydelik TTI). In 'n klassieke scenario wat uiteindelik in 2013 by Airbnb geformuleer jaar (nog steeds op Backbone.js), SSR is dieselfde isomorfiese JS-toepassing wat in die Node-omgewing loop. Die bediener stuur eenvoudig die gegenereerde uitleg as 'n reaksie op die versoek. Dan vind rehidrasie plaas aan die kliëntkant, en dan werk alles sonder om bladsye te herlaai. Vir Habr, soos vir baie ander hulpbronne met teksinhoud, is bedienerweergawe 'n kritieke element in die bou van vriendelike verhoudings met soekenjins.

Ten spyte van die feit dat meer as ses jaar verloop het sedert die koms van die tegnologie, en gedurende hierdie tyd het baie water regtig onder die brug in die front-end wêreld gevlieg, is hierdie idee vir baie ontwikkelaars steeds in geheimhouding gehul. Ons het nie opsy gestaan ​​nie en 'n Vue-toepassing met SSR-ondersteuning na produksie uitgerol, en een klein detail ontbreek: ons het nie die aanvanklike toestand aan die kliënt gestuur nie.

Hoekom? Daar is geen presiese antwoord op hierdie vraag nie. Óf hulle wou nie die grootte van die reaksie vanaf die bediener vergroot nie, óf as gevolg van 'n klomp ander argitektoniese probleme, óf dit het eenvoudig nie begin nie. Op een of ander manier lyk dit redelik gepas en nuttig om die staat uit te gooi en alles wat die bediener gedoen het te hergebruik. Die taak is eintlik triviaal - toestand word eenvoudig ingespuit in die uitvoeringskonteks, en Vue voeg dit outomaties by die gegenereerde uitleg as 'n globale veranderlike: window.__INITIAL_STATE__.

Een van die probleme wat ontstaan ​​het, is die onvermoë om sikliese strukture in JSON (omsendbrief verwysing); is opgelos deur bloot sulke strukture met hul plat eweknieë te vervang.

Daarbenewens, wanneer u met UGC-inhoud handel, moet u onthou dat die data na HTML-entiteite omgeskakel moet word om nie die HTML te breek nie. Vir hierdie doeleindes gebruik ons he.

Minimaliseer hertekeninge

Soos u uit die diagram hierbo kan sien, voer een Node JS-instansie in ons geval twee funksies uit: SSR en "proxy" in die API, waar gebruikermagtiging plaasvind. Hierdie omstandigheid maak dit onmoontlik om te magtig terwyl die JS-kode op die bediener loop, aangesien die nodus enkeldraad is en die SSR-funksie sinchronies is. Dit wil sê, die bediener kan eenvoudig nie versoeke na homself stuur terwyl die oproepstapel met iets besig is nie. Dit het geblyk dat ons die toestand opgedateer het, maar die koppelvlak het nie ophou ruk nie, aangesien die data op die kliënt opgedateer moes word met inagneming van die gebruikerssessie. Ons moes ons toepassing leer om die korrekte data in die aanvanklike toestand te plaas, met inagneming van die gebruiker se aanmelding.

Daar was net twee oplossings vir die probleem:

  • heg magtigingsdata aan kruisbedienerversoeke;
  • verdeel Node JS-lae in twee afsonderlike gevalle.

Die eerste oplossing het die gebruik van globale veranderlikes op die bediener vereis, en die tweede het die sperdatum vir die voltooiing van die taak met minstens 'n maand verleng.

Hoe om 'n keuse te maak? Habr beweeg dikwels langs die pad van minste weerstand. Informeel is daar 'n algemene begeerte om die siklus van idee tot prototipe tot 'n minimum te verminder. Die model van houding teenoor die produk herinner ietwat aan die postulate van booking.com, met die enigste verskil dat Habr gebruikersterugvoer baie ernstiger opneem en jou as ontwikkelaar vertrou om sulke besluite te neem.

Na aanleiding van hierdie logika en my eie begeerte om die probleem vinnig op te los, het ek globale veranderlikes gekies. En, soos dikwels gebeur, moet jy vroeër of later daarvoor betaal. Ons het amper dadelik betaal: ons het die naweek gewerk, die gevolge opgeklaar, geskryf nadoodse en begin om die bediener in twee dele te verdeel. Die fout was baie dom, en die fout wat dit behels, was nie maklik om te reproduseer nie. En ja, dit is jammer hiervoor, maar op een of ander manier, struikelend en kreunend, het my PoC met globale veranderlikes nietemin in produksie gegaan en werk redelik suksesvol terwyl ek wag vir die skuif na 'n nuwe "twee-node" argitektuur. Dit was 'n belangrike stap, want formeel is die doel bereik - SSR het geleer om 'n heeltemal gereed-vir-gebruik bladsy te lewer, en die UI het baie rustiger geword.

Habr front-end ontwikkelaar logs: herfaktorering en reflekteringMobile Habr-koppelvlak na die eerste fase van herfaktorering

Uiteindelik lei die SSR-CSR-argitektuur van die mobiele weergawe tot hierdie prentjie:

Habr front-end ontwikkelaar logs: herfaktorering en reflektering"Twee-node" SSR-CSR stroombaan. Die Node JS API is altyd gereed vir asynchrone I/O en word nie deur die SSR-funksie geblokkeer nie, aangesien laasgenoemde in 'n aparte geval geleë is. Navraagketting #3 is nie nodig nie.

Uitskakeling van duplikaatversoeke

Nadat die manipulasies uitgevoer is, het die aanvanklike weergawe van die bladsy nie meer epilepsie uitgelok nie. Maar die verdere gebruik van Habr in SPA-modus het steeds verwarring veroorsaak.

Aangesien die basis van gebruikersvloei oorgange van die vorm is lys van artikels → artikel → opmerkings en omgekeerd, dit was belangrik om die hulpbronverbruik van hierdie ketting in die eerste plek te optimaliseer.

Habr front-end ontwikkelaar logs: herfaktorering en reflekteringOm terug te keer na die plasingstroom ontlok 'n nuwe dataversoek

Dit was nie nodig om diep te delf nie. In die skermuitsending hierbo kan jy sien dat die toepassing die lys van artikels weer aanvra wanneer jy terugvee, en tydens die versoek sien ons nie die artikels nie, wat beteken dat die vorige data iewers verdwyn. Dit lyk of die artikellys-komponent 'n plaaslike staat gebruik en dit verloor wanneer dit vernietig word. Trouens, die toepassing het 'n globale toestand gebruik, maar die Vuex-argitektuur is reguit gebou: modules is aan bladsye gekoppel, wat weer aan roetes gekoppel is. Boonop is alle modules "weggooibaar" - elke daaropvolgende besoek aan die bladsy het die hele module herskryf:

ArticlesList: [
  { Article1 },
  ...
],
PageArticle: { ArticleFull1 },

In totaal het ons 'n module gehad Artikellys, wat voorwerpe van tipe bevat Artikel en module Bladsy-artikel, wat 'n uitgebreide weergawe van die voorwerp was Artikel, soort van ArtikelVol. Oor die algemeen dra hierdie implementering niks verskrikliks op sigself nie - dit is baie eenvoudig, 'n mens kan selfs sê naïef, maar uiters verstaanbaar. As jy die module terugstel elke keer as jy die roete verander, dan kan jy selfs daarmee saamleef. Beweeg egter tussen artikelstrome, byvoorbeeld /voer → /almal, is gewaarborg om alles wat verband hou met die persoonlike voer weg te gooi, aangesien ons net een het Artikellys, waarin jy nuwe data moet plaas. Dit lei ons weer tot duplisering van versoeke.

Nadat ek alles versamel het wat ek oor die onderwerp kon opgrawe, het ek 'n nuwe staatstruktuur geformuleer en dit aan my kollegas voorgelê. Die besprekings was lank, maar uiteindelik het die argumente ten gunste die twyfel oortref, en ek het met implementering begin.

Die logika van 'n oplossing word die beste in twee stappe geopenbaar. Eerstens probeer ons om die Vuex-module van bladsye te ontkoppel en direk aan roetes te bind. Ja, daar sal 'n bietjie meer data in die winkel wees, getters sal 'n bietjie meer kompleks word, maar ons sal nie artikels twee keer laai nie. Vir die mobiele weergawe is dit miskien die sterkste argument. Dit sal so iets lyk:

ArticlesList: {
  ROUTE_FEED: [ 
    { Article1 },
    ...
  ],
  ROUTE_ALL: [ 
    { Article2 },
    ...
  ],
}

Maar wat as artikellyste tussen verskeie roetes kan oorvleuel en wat as ons objekdata wil hergebruik Artikel om die posbladsy weer te gee, om dit in te verander ArtikelVol? In hierdie geval sal dit meer logies wees om so 'n struktuur te gebruik:

ArticlesIds: {
  ROUTE_FEED: [ '1', ... ],
  ROUTE_ALL: [ '1', '2', ... ],
},
ArticlesList: {
  '1': { Article1 }, 
  '2': { Article2 },
  ...
}

Artikellys hier is dit net 'n soort bewaarplek van artikels. Alle artikels wat tydens die gebruikersessie afgelaai is. Ons behandel hulle met die grootste sorg, want dit is verkeer wat dalk deur pyn iewers in die metro tussen stasies afgelaai is, en ons wil beslis nie weer hierdie pyn vir die gebruiker veroorsaak deur hom te dwing om data te laai wat hy reeds het nie afgelaai. 'n Voorwerp Artikel-ID's is bloot 'n reeks ID's (asof "skakels") na voorwerpe Artikel. Hierdie struktuur laat jou toe om duplisering van data wat algemeen is aan roetes te vermy en die hergebruik van die voorwerp Artikel wanneer 'n plasingsbladsy weergegee word deur uitgebreide data daarin saam te voeg.

Die afvoer van die lys van artikels het ook meer deursigtig geword: die iterator-komponent itereer deur die skikking met artikel-ID's en teken die artikel-teaser-komponent, deur die ID as 'n rekwisiet deur te gee, en die kind-komponent haal op sy beurt die nodige data van Artikellys. Wanneer jy na die publikasiebladsy gaan, kry ons die reeds bestaande datum vanaf Artikellys, rig ons 'n versoek om die ontbrekende data te bekom en voeg dit eenvoudig by die bestaande voorwerp.

Hoekom is hierdie benadering beter? Soos ek hierbo geskryf het, is hierdie benadering sagter met betrekking tot die afgelaaide data en laat jou toe om dit te hergebruik. Maar behalwe dit, maak dit die weg oop vir 'n paar nuwe moontlikhede wat perfek in so 'n argitektuur pas. Byvoorbeeld, peiling en laai van artikels in die stroom soos dit verskyn. Ons kan eenvoudig die nuutste plasings in 'n "berging" plaas Artikellys, stoor 'n aparte lys van nuwe ID's in Artikel-ID's en stel die gebruiker daaroor in kennis. Wanneer ons op die "Wys nuwe publikasies"-knoppie klik, sal ons eenvoudig nuwe Id's in die begin van die reeks van die huidige lys van artikels invoeg en alles sal amper magies werk.

Maak aflaai lekkerder

Die kersie op die hervormingskoek is die konsep van geraamtes, wat die proses om inhoud op 'n stadige internet af te laai 'n bietjie minder walglik maak. Daar was geen besprekings oor hierdie saak nie; die pad van idee tot prototipe het letterlik twee uur geneem. Die ontwerp het feitlik homself geteken, en ons het ons komponente geleer om eenvoudige, skaars flikkerende div-blokke weer te gee terwyl ons vir data wag. Subjektief, verminder hierdie benadering tot laai eintlik die hoeveelheid streshormone in die gebruiker se liggaam. Die skelet lyk so:

Habr front-end ontwikkelaar logs: herfaktorering en reflektering
Habraloading

Reflekteer

Ek werk al ses maande in Habré en my vriende vra steeds: wel, hoe hou jy daarvan daar? Goed, gemaklik - ja. Maar daar is iets wat hierdie werk anders maak as ander. Ek het in spanne gewerk wat heeltemal onverskillig was oor hul produk, nie geweet of verstaan ​​het wie hul gebruikers is nie. Maar hier is alles anders. Hier voel jy verantwoordelik vir wat jy doen. In die proses om 'n kenmerk te ontwikkel, word jy gedeeltelik die eienaar daarvan, neem deel aan alle produkvergaderings wat met jou funksionaliteit verband hou, maak voorstelle en neem self besluite. Om self 'n produk te maak wat jy elke dag gebruik is baie gaaf, maar om kode te skryf vir mense wat waarskynlik beter daarmee as jy is, is net 'n ongelooflike gevoel (geen sarkasme).

Na die vrystelling van al hierdie veranderinge het ons positiewe terugvoer ontvang, en dit was baie, baie lekker. Dis inspirerend. Dankie! Skryf meer.

Laat ek u daaraan herinner dat ons na globale veranderlikes besluit het om die argitektuur te verander en die proxy-laag in 'n aparte instansie toe te wys. Die "twee-node"-argitektuur het reeds vrystelling bereik in die vorm van openbare beta-toetsing. Nou kan enigiemand daarna oorskakel en ons help om mobiele Habr beter te maak. Dis al vir vandag. Ek sal met graagte al jou vrae in die kommentaar beantwoord.

Bron: will.com

Voeg 'n opmerking