Habr front-end ûntwikkelderslogboeken: refactoring en reflecting

Habr front-end ûntwikkelderslogboeken: refactoring en reflecting

Ik haw altyd ynteressearre west yn hoe't Habr fan binnen strukturearre is, hoe't de workflow strukturearre is, hoe't kommunikaasje strukturearre is, hokker noarmen brûkt wurde en hoe't koade hjir oer it algemien skreaun wurdt. Gelokkich krige ik sa'n kâns, want ik bin koartlyn diel útmakke fan it habrateam. Mei it foarbyld fan in lytse refactoring fan 'e mobile ferzje, sil ik besykje de fraach te beantwurdzjen: hoe is it om hjir oan 'e foarkant te wurkjen. Yn it programma: Node, Vue, Vuex en SSR mei saus út oantekeningen oer persoanlike ûnderfining yn Habr.

It earste ding dat jo moatte witte oer it ûntwikkelingsteam is dat d'r in pear fan ús binne. Net genôch - dit binne trije fronten, twa efterkanten en de technyske foarsprong fan alle Habr - Baxley. Der is fansels ek in tester, in ûntwerper, trije Vadim, in wûnderbiezem, in marketingspesjalist en oare Bumburums. Mar d'r binne mar seis direkte bydragen oan Habr's boarnen. Dit is frij seldsum - in projekt mei in multymiljoen-dollar publyk, dat fan bûten liket op in gigantyske ûndernimming, yn werklikheid liket mear op in gesellige startup mei de flakste organisaasjestruktuer mooglik.

Lykas in protte oare IT-bedriuwen beliedt Habr Agile ideeën, CI-praktiken, en dat is alles. Mar neffens myn gefoelens ûntwikkelet Habr as produkt mear yn golven dan kontinu. Dus, foar ferskate sprints op in rige, kodearje wy iverich wat, ûntwerpe en opnij ûntwerpe, brekke wat en reparearje it, lossen kaartsjes op en meitsje nije, stappe op in hark en sjitte ússels yn 'e fuotten, om de funksje úteinlik frij te litten yn produksje. En dan komt der in bepaalde stilte, in perioade fan werynrjochting, tiid om te dwaan wat yn it "wichtich-net urgent" kwadrant stiet.

It is krekt dizze "off-season" sprint dy't hjirûnder besprutsen wurde. Dizze kear omfette it in refactoring fan 'e mobile ferzje fan Habr. Yn 't algemien hat it bedriuw hege hope foar it, en yn' e takomst moat it de hiele bistetún fan 'e ynkarnaasjes fan Habr ferfange en in universele cross-platform-oplossing wurde. Ienris sil d'r adaptive yndieling, PWA, offline modus, oanpassing fan brûkers, en in protte oare nijsgjirrige dingen wêze.

Litte wy de taak ynstelle

Ienris, by in gewoane stand-up, spruts ien fan 'e fronten oer problemen yn' e arsjitektuer fan 'e opmerkingskomponint fan' e mobile ferzje. Mei dit yn gedachten hawwe wy in mikrogearkomste organisearre yn de foarm fan groepspsychotherapy. Elkenien om beurten te sizzen wêr't it sear die, se hawwe alles op papier skreaun, se sympatisearren, se begrepen, útsein dat nimmen klapte. It resultaat wie in list fan 20 problemen, dy't dúdlik makke dat mobile Habr noch in lang en steklik paad nei súkses hie.

Ik wie primêr soargen oer de effisjinsje fan boarnegebrûk en wat in soepele ynterface neamd wurdt. Elke dei, op 'e hûs-wurk-thús-rûte, seach ik myn âlde tillefoan wanhopich besykje 20 koppen yn' e feed wer te jaan. It seach der sa út:

Habr front-end ûntwikkelderslogboeken: refactoring en reflectingMobile Habr-ynterface foar refactoring

Wat bart hjir? Koartsein, de server tsjinne de HTML-side oan elkenien op deselde manier, nettsjinsteande oft de brûker oanmeld wie of net. Dan de klant JS wurdt laden en freget de nedige gegevens wer, mar oanpast foar autorisaasje. Dat is, wy hawwe eins twa kear itselde wurk dien. De ynterface flikkere, en de brûker downloadde goed hûndert ekstra kilobytes. Yn detail alles seach noch mear griezelig.

Habr front-end ûntwikkelderslogboeken: refactoring en reflectingAlde SSR-CSR skema. Autorisaasje is allinich mooglik yn 'e stadia C3 en C4, as Node JS net drok is mei it generearjen fan HTML en kin proxy-fersiken nei de API.

Us arsjitektuer fan dy tiid waard tige sekuer beskreaun troch ien fan 'e Habr brûkers:

De mobile ferzje is crap. Ik fertel it sa as it is. In skriklike kombinaasje fan SSR en CSR.

Wy moasten it tajaan, hoe tryst it ek wie.

Ik beoardiele de opsjes, makke in kaartsje yn Jira mei in beskriuwing op it nivo fan "it is no min, doch it goed" en ferdielde de taak yn grutte streken:

  • gegevens opnij brûke,
  • minimalisearje it oantal redraws,
  • eliminearje dûbele oanfragen,
  • meitsje it laden proses dúdliker.

Litte wy de gegevens opnij brûke

Yn teory is rendering oan serverside ûntworpen om twa problemen op te lossen: net te lijen fan beheiningen fan sykmasjines yn termen fan SPA-yndeksearring en ferbetterje de metryske FMP (ûnferjitlik fergriemd TTI). Yn in klassyk senario dat úteinlik formulearre by Airbnb yn 2013 jier (noch op Backbone.js), SSR is deselde isomorphic JS applikaasje rint yn de Node omjouwing. De tsjinner stjoert gewoan de oanmakke opmaak as antwurd op it fersyk. Dan komt rehydratisaasje foar oan 'e kant fan' e kliïnt, en dan wurket alles sûnder side opnij laden. Foar Habr, lykas foar in protte oare boarnen mei tekstynhâld, is serverrendering in kritysk elemint by it bouwen fan freonlike relaasjes mei sykmasines.

Nettsjinsteande it feit dat mear as seis jier binne foarby sûnt de komst fan de technology, en yn dizze tiid in protte wetter is echt flein ûnder de brêge yn 'e front-end wrâld, foar in protte ûntwikkelders dit idee is noch altyd bedutsen yn geheimhâlding. Wy stiene net oan 'e kant en rôle in Vue-applikaasje mei SSR-stipe út nei produksje, ûntbrekkende ien lyts detail: wy hawwe de earste steat net nei de kliïnt stjoerd.

Wêrom? D'r is gjin krekte antwurd op dizze fraach. Of se woene de grutte fan 'e reaksje fan' e tsjinner net ferheegje, of fanwege in bosk oare arsjitektoanyske problemen, of it naam gewoan net ôf. Op ien of oare manier, it útjaan fan steat en opnij brûke fan alles wat de tsjinner die, liket heul passend en nuttich. De taak is eins triviaal - steat wurdt gewoan ynjeksje yn 'e útfieringskontekst, en Vue foeget it automatysk ta oan' e generearre yndieling as in globale fariabele: window.__INITIAL_STATE__.

Ien fan 'e problemen dy't ûntstien is is it ûnfermogen om siklyske struktueren te konvertearjen yn JSON (sirkulêre referinsje); waard oplost troch gewoan te ferfangen sokke struktueren mei harren platte tsjinhingers.

Derneist, as jo mei UGC-ynhâld omgean, moatte jo betinke dat de gegevens moatte wurde omboud ta HTML-entiteiten om de HTML net te brekken. Foar dizze doelen brûke wy he.

Minimalisearjen fan redraws

Sa't jo kinne sjen út it diagram hjirboppe, yn ús gefal, ien Node JS eksimplaar fiert twa funksjes: SSR en "proxy" yn de API, dêr't brûker autorisaasje foarkomt. Dizze omstannichheid makket it ûnmooglik om te autorisearjen wylst de JS-koade op 'e tsjinner rint, om't it knooppunt is single-threaded, en de SSR-funksje is syngroan. Dat is, de tsjinner kin gewoan gjin fersiken nei himsels stjoere wylst de callstack mei wat dwaande is. It die bliken dat wy de steat bywurke, mar de ynterface stoppe net mei twitching, om't de gegevens op 'e kliïnt moasten wurde bywurke mei rekkening mei de brûkerssesje. Wy moasten ús applikaasje leare om de juste gegevens yn 'e begjinstân te setten, rekken hâldend mei de oanmelding fan 'e brûker.

D'r wiene mar twa oplossingen foar it probleem:

  • taheakje autorisaasje gegevens oan cross-server fersiken;
  • split Node JS lagen yn twa aparte eksimplaren.

De earste oplossing fereasket it brûken fan globale fariabelen op 'e tsjinner, en de twadde ferlingde de deadline foar it foltôgjen fan de taak mei op syn minst in moanne.

Hoe meitsje in kar? Habr beweecht faak lâns it paad fan minste wjerstân. Ynformeel is der in algemiene winsk om de syklus fan idee nei prototype oant in minimum te ferminderjen. It model fan hâlding foar it produkt docht wat tinken oan 'e postulaten fan booking.com, mei it iennichste ferskil dat Habr brûkersfeedback folle serieuzer nimt en jo fertrout, as ûntwikkelder, om sokke besluten te nimmen.

Nei dizze logika en myn eigen winsk om it probleem fluch op te lossen, keas ik globale fariabelen. En, lykas faaks bart, moatte jo ier of let foar har betelje. Wy betellen hast fuortendaliks: wy wurken yn it wykein, hawwe de gefolgen opromme, skreaun post-mortem en begûn te ferdielen de tsjinner yn twa dielen. De flater wie tige dom, en de brek wêrby't it wie net maklik te reprodusearjen. En ja, it is spitich foar dit, mar op ien of oare manier, stroffeljend en kreunend, gie myn PoC mei wrâldwide fariabelen dochs yn produksje en wurket frij suksesfol wylst ik wachtet op de ferhuzing nei in nije "twa-knooppunt" arsjitektuer. Dit wie in wichtige stap, om't formeel it doel waard berikt - SSR learde in folslein klear te brûken side te leverjen, en de UI waard folle rêstiger.

Habr front-end ûntwikkelderslogboeken: refactoring en reflectingMobile Habr ynterface nei de earste faze fan refactoring

Uteinlik liedt de SSR-CSR-arsjitektuer fan 'e mobile ferzje ta dizze foto:

Habr front-end ûntwikkelderslogboeken: refactoring en reflecting"Twa-knooppunt" SSR-CSR circuit. De Node JS API is altyd klear foar asynchrone I/O en wurdt net blokkearre troch de SSR-funksje, om't de lêste yn in apart eksimplaar leit. Query ketting #3 is net nedich.

It eliminearjen fan dûbele oanfragen

Nei't de manipulaasjes waarden útfierd, feroarsake de earste werjefte fan 'e side gjin epilepsy mear. Mar it fierdere gebrûk fan Habr yn SPA-modus soarge noch foar betizing.

Sûnt de basis fan brûkersstream is oergongen fan 'e foarm list fan artikels → artikel → opmerkings en oarsom, it wie wichtich om te optimalisearjen de boarne konsumpsje fan dizze keten yn it foarste plak.

Habr front-end ûntwikkelderslogboeken: refactoring en reflectingWerom nei de postfeed provosearret in nij gegevensfersyk

Der wie gjin need om djip te graven. Yn 'e skermcast hjirboppe kinne jo sjen dat de applikaasje de list mei artikels opnij freget as jo werom swipe, en tidens it fersyk sjogge wy de artikels net, wat betsjut dat de foarige gegevens earne ferdwine. It liket derop dat de komponint fan 'e artikellist in lokale steat brûkt en it ferliest op ferneatigjen. Yn feite brûkte de applikaasje in wrâldwide steat, mar de Vuex-arsjitektuer waard frontaal boud: modules binne bûn oan siden, dy't op har beurt bûn binne oan rûtes. Boppedat binne alle modules "wegwerp" - elke folgjende besite oan 'e side skreau de heule module opnij:

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

Yn totaal hiene wy ​​in module List fan artikels, dy't objekten fan type befettet Lidwurd en module PageArtikel, dat wie in útwreide ferzje fan it objekt Lidwurd, soart fan ArtikelFol. Yn it algemien, dizze ymplemintaasje draacht neat ferskrikliks op himsels - it is hiel simpel, men soe sels sizze naïve, mar ekstreem begryplik. As jo ​​de module weromsette elke kear as jo de rûte feroarje, dan kinne jo der sels mei libje. Lykwols, bygelyks, ferpleatse tusken artikel feeds /feed → /alles, wurdt garandearre te smiten alles yn ferbân mei de persoanlike feed, sûnt wy hawwe mar ien List fan artikels, wêryn jo nije gegevens moatte pleatse. Dit liedt ús wer ta duplikaasje fan oanfragen.

Nei't ik alles sammele dat ik koe opgrave oer it ûnderwerp, formulearre ik in nije steatstruktuer en presintearre it oan myn kollega's. De diskusjes wiene lang, mar op it lêst wiene de arguminten foar it foardiel de twifels, en ik begon mei útfiering.

De logika fan in oplossing wurdt it bêste iepenbiere yn twa stappen. Earst besykje wy de Vuex-module te ûntkoppelen fan siden en direkt te binen oan rûtes. Ja, d'r sille in bytsje mear gegevens yn 'e winkel wêze, getters sille in bytsje komplekser wurde, mar wy sille artikels net twa kear lade. Foar de mobile ferzje is dit faaks it sterkste argumint. It sil der sa útsjen:

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

Mar wat as artikellisten oerlappe kinne tusken meardere rûtes en wat as wy objektgegevens opnij wolle brûke Lidwurd om de berjochtside wer te jaan, it omsette yn ArtikelFol? Yn dit gefal soe it logysker wêze om sa'n struktuer te brûken:

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

List fan artikels hjir is it gewoan in soarte fan depot fan artikels. Alle artikels dy't waarden ynladen tidens de brûkerssesje. Wy behannelje se mei de uterste soarch, om't dit ferkear is dat troch pine earne yn 'e metro tusken stasjons ynladen is, en wy wolle de brûker perfoarst net wer dizze pine feroarsaakje troch him te twingen gegevens te laden dy't hy al hat ynladen. In objekt ArticlesIds is gewoan in array fan ID's (as as "keppelings") nei objekten Lidwurd. Dizze struktuer lit jo it duplikearjen fan gegevens gewoanlik foar rûtes foarkomme en it objekt opnij brûke Lidwurd by it werjaan fan in postside troch útwreide gegevens dêryn te fusearjen.

De útfier fan 'e list mei artikels is ek transparanter wurden: it iterator-ûnderdiel iterearret troch de array mei artikel-ID's en tekenet de artikelteaser-komponint, troch de Id troch te jaan as prop, en it bernûnderdiel hellet op syn beurt de nedige gegevens fan List fan artikels. As jo ​​​​nei de publikaasjeside gean, krije wy de al besteande datum fan List fan artikels, meitsje wy in fersyk om de ûntbrekkende gegevens te krijen en it gewoan tafoegje oan it besteande objekt.

Wêrom is dizze oanpak better? Lykas ik hjirboppe skreau, is dizze oanpak sêfter mei respekt foar de ynladen gegevens en lit jo it opnij brûke. Mar boppedat iepenet it de wei nei inkele nije mooglikheden dy't perfekt passe yn sa'n arsjitektuer. Bygelyks, polling en laden fan artikels yn 'e feed sa't se ferskine. Wy kinne de lêste berjochten gewoan yn in "opslach" pleatse List fan artikels, bewarje in aparte list mei nije ID's yn ArticlesIds en ynformearje de brûker deroer. As wy op de knop "Nije publikaasjes sjen litte" klikke, sille wy gewoan nije ID's ynfoegje yn it begjin fan 'e array fan' e hjoeddeistige list mei artikels en alles sil hast magysk wurkje.

It downloaden nofliker meitsje

De kers op 'e refactoring-cake is it konsept fan skeletten, wat it proses fan it downloaden fan ynhâld op in traach ynternet in bytsje minder walgelijk makket. D'r wiene gjin diskusjes oer dizze saak; it paad fan idee nei prototype naam letterlik twa oeren. It ûntwerp tekene himsels praktysk, en wy learden ús komponinten om ienfâldige, amper flikkerjende div-blokken te meitsjen wylst wy wachtsje op gegevens. Subjektyf ferminderet dizze oanpak fan laden eins it bedrach fan stresshormonen yn it lichem fan 'e brûker. It skelet sjocht der sa út:

Habr front-end ûntwikkelderslogboeken: refactoring en reflecting
Habraloading

Refleksje

Ik wurkje al seis moanne yn Habré en myn freonen freegje noch: no, hoe fynst it dêr? Goed, noflik - ja. Mar der is wat dat makket dit wurk oars as oaren. Ik wurke yn teams dy't folslein ûnferskillich wiene foar har produkt, net wisten of begrepen wa't har brûkers wiene. Mar hjir is alles oars. Hjir fiele jo jo ferantwurdlik foar wat jo dogge. Yn it proses fan it ûntwikkeljen fan in funksje wurde jo foar in part syn eigner, nimme diel oan alle produktgearkomsten yn ferbân mei jo funksjonaliteit, meitsje suggestjes en meitsje sels besluten. In produkt meitsje dat jo sels elke dei brûke is heul cool, mar it skriuwen fan koade foar minsken dy't der wierskynlik better yn binne as jo is gewoan in ongelooflijk gefoel (gjin sarkasme).

Nei de frijlitting fan al dizze feroarings, wy krigen positive feedback, en it wie hiel, hiel moai. It is ynspirearjend. Dankewol! Skriuw mear.

Lit my jo herinnerje dat wy nei globale fariabelen besletten hawwe de arsjitektuer te feroarjen en de proxy-laach yn in aparte eksimplaar te allocearjen. De "twa-knooppunt" arsjitektuer hat al frijlitting berikt yn 'e foarm fan iepenbiere beta-testen. No kin elkenien derop oerskeakelje en ús helpe om mobile Habr better te meitsjen. Dat is alles foar hjoed. Ik sil bliid wêze om al jo fragen te beantwurdzjen yn 'e opmerkings.

Boarne: www.habr.com

Add a comment