Protokoly front-end vývojárov Habr: refaktorovanie a reflektovanie

Protokoly front-end vývojárov Habr: refaktorovanie a reflektovanie

Vždy ma zaujímalo, ako je Habr štruktúrovaný zvnútra, ako je štruktúrovaný pracovný postup, ako je štruktúrovaná komunikácia, aké štandardy sa používajú a ako sa tu všeobecne píše kód. Našťastie som dostal túto príležitosť, pretože som sa nedávno stal súčasťou tímu habra. Na príklade malého refaktoringu mobilnej verzie sa pokúsim odpovedať na otázku: aké je to tu pracovať v prvej línii? V programe: Node, Vue, Vuex a SSR s omáčkou z poznámok o osobnej skúsenosti v Habr.

Prvá vec, ktorú potrebujete vedieť o vývojárskom tíme, je, že sme malí. Malý je tri predné, dva zadné a technický náskok všetkých Habr - Baxley. Nechýba samozrejme ani tester, dizajnér, traja Vadimovia, zázračná metla, marketér a ďalší Bumburovia. Na Habrove stránky je však iba šesť priamych prispievateľov. To je dosť zriedkavé – projekt s multimiliónovým publikom, ktorý navonok vyzerá ako obrovský podnik, je v skutočnosti skôr útulným startupom s najplochejšou organizačnou štruktúrou.

Rovnako ako mnoho iných IT spoločností, aj Habr vyznáva agilné nápady, praktiky CI a to je všetko. Ale podľa mňa sa Habr ako produkt vyvíja skôr vo vlnách ako kontinuálne. Niekoľko šprintov za sebou teda usilovne niečo kódujeme, navrhujeme a redizajnujeme, niečo rozbíjame a opravujeme, riešime tikety a vytvárame nové, šliapeme na hrable a strieľame si do nôh, aby sme túto funkciu konečne uvoľnili v budúcnosti. A potom príde určitý pokoj, obdobie prestavby, čas urobiť to, čo je v kvadrante „dôležité – nie naliehavé“.

Presne o tomto „mimosezónnom“ šprinte bude reč nižšie. Tentoraz zahŕňal refaktoring mobilnej verzie Habr. Vo všeobecnosti do nej spoločnosť vkladá veľké nádeje a v budúcnosti by mala nahradiť celú zoologickú záhradu Habrových inkarnácií a stať sa univerzálnym multiplatformovým riešením. Jedného dňa tu bude adaptívne rozloženie, PWA, offline režim, užívateľské prispôsobenie a mnoho ďalších zaujímavých vecí.

Stanovme si úlohu

Raz na obyčajnom stand-upe jeden z predných hovoril o problémoch v architektúre komponentu komentárov mobilnej verzie. S týmto podnetom sme zorganizovali mikrostretnutie vo formáte skupinovej psychoterapie. Všetci sa striedali, kde to bolelo, všetko si zapisovali na papier, sympatizovali, rozumeli, až na to, že nikto netlieskal. Výsledkom bol zoznam 20 problémov, z ktorých bolo jasné, že mobilného Habra čaká ešte dlhá a tŕnistá cesta k úspechu.

Hlavne som sa obával efektívnosti zdrojov a toho, čo sa nazýva hladké rozhranie. Každý deň som cestou „domov-práca-domov“ videl svoj starý telefón, ako sa zúfalo pokúšal zobraziť 20 titulkov v informačnom kanáli. Vyzeralo to asi takto:

Protokoly front-end vývojárov Habr: refaktorovanie a reflektovanieRozhranie Mobile Habr pred refaktorizáciou

Čo sa tu deje? Stručne povedané, server poskytol HTML stránku všetkým rovnakým spôsobom, bez ohľadu na to, či bol používateľ prihlásený alebo nie. Potom sa načíta klientsky JS a znova požaduje potrebné údaje, tentokrát však upravené na autorizáciu. To znamená, že sme tú istú prácu robili dvakrát. Rozhranie zablikalo a používateľ si stiahol dobrých sto kilobajtov navyše. V detailoch všetko vyzeralo ešte strašidelnejšie.

Protokoly front-end vývojárov Habr: refaktorovanie a reflektovanieStará schéma SSR-CSR. Autorizácia je možná len vo fázach C3 a C4, keď Node JS nie je zaneprázdnený generovaním HTML a môže proxy požiadavky do API.

Našu vtedajšiu architektúru veľmi presne opísal jeden z užívateľov Habr:

Mobilná verzia je svinstvo. Hovorím to tak, ako to je. Otrasná kombinácia SSR spolu s CSR.

Boli sme nútení to priznať, bez ohľadu na to, aké smutné to bolo.

Posúdil som možnosti, vytvoril som tiket v Jire s popisom na úrovni „teraz je to zlé, nech je to v poriadku“ a rozložil som úlohu širokými ťahmi:

  • znovu použiť dáta,
  • minimalizovať počet prekreslení,
  • eliminovať duplicitné požiadavky,
  • zjednodušiť proces načítania.

Poďme znova použiť dáta

Teoreticky je vykresľovanie na strane servera navrhnuté tak, aby vyriešilo dva problémy: netrpieť obmedzeniami vyhľadávacieho nástroja SPA indexácia a zlepšiť metriky FMP (nevyhnutne sa zhoršuje TTI). V klasickom scenári, že konečne formulované v Airbnb v roku 2013 rok (späť na Backbone.js), SSR je rovnaká izomorfná JS aplikácia bežiaca v prostredí Node. Server jednoducho vráti vygenerované rozloženie ako odpoveď na požiadavku. Potom dôjde k rehydratácii na strane klienta a potom všetko funguje bez opätovného načítania stránky. Pre Habra, ako aj pre mnohé iné zdroje s textovým obsahom, je serverové vykresľovanie kritickým prvkom pri budovaní priateľských vzťahov s vyhľadávačmi.

Napriek tomu, že od objavenia sa technológie ubehlo už viac ako šesť rokov a za tento čas prešlo pod mostom vo front-end svete veľa vody, pre mnohých vývojárov je táto myšlienka stále zahalená rúškom tajomstva. Nestáli sme bokom a spustili sme aplikáciu Vue s podporou SSR do produkcie, pričom nám chýbal jeden malý detail: klientovi sme neodovzdali počiatočný stav.

prečo? Na túto otázku neexistuje presná odpoveď. Buď nechceli zväčšiť veľkosť odpovede zo servera, alebo kvôli množstvu iných architektonických problémov, alebo sa to jednoducho nerozbehlo. Tak či onak, vyhodiť stav a znova použiť všetko, čo server urobil, sa zdá byť celkom vhodné a užitočné. Úloha je vlastne triviálna. stav sa jednoducho vstrekne do kontextu vykonávania a Vue ho automaticky pridá do vygenerovaného rozloženia ako globálnu premennú: window.__INITIAL_STATE__.

Jedným z problémov, ktoré sa objavili, je neschopnosť konvertovať cyklické štruktúry na JSON (kruhový odkaz); bola vyriešená jednoduchým nahradením takýchto štruktúr ich plochými náprotivkami.

Okrem toho by ste pri práci s obsahom UGC mali pamätať na to, že údaje by sa mali previesť na entity HTML, aby nedošlo k porušeniu kódu HTML. Na tieto účely používame he.

Minimalizácia prekreslení

Ako môžete vidieť z vyššie uvedeného diagramu, v našom prípade jedna inštancia Node JS vykonáva dve funkcie: SSR a „proxy“ v API, kde dochádza k autorizácii používateľa. Táto okolnosť znemožňuje autorizáciu, kým je kód JS spustený na serveri, pretože uzol je jednovláknový a funkcia SSR je synchrónna. To znamená, že server jednoducho nemôže posielať požiadavky sám sebe, kým je zásobník hovorov niečím zaneprázdnený. Ukázalo sa, že sme aktualizovali stav, ale rozhranie neprestalo trhať, pretože údaje o klientovi sa museli aktualizovať s prihliadnutím na reláciu používateľa. Našu aplikáciu bolo potrebné naučiť uvádzať správne údaje do počiatočného stavu s prihliadnutím na prihlásenie užívateľa.

Existovali len dve riešenia problému:

  • prepojiť autorizačné údaje s požiadavkami medzi servermi;
  • rozdeliť vrstvy Node JS na dve samostatné inštancie.

Prvé riešenie vyžadovalo použitie globálnych premenných na serveri a druhé predĺžilo časový rámec na dokončenie úlohy minimálne o mesiac.

Ako si vybrať? Habr sa často pohybuje po ceste najmenšieho odporu. Neformálne existuje všeobecná túžba znížiť cyklus od nápadu k prototypu na minimum. Model postoja k produktu trochu pripomína postuláty booking.com, len s tým rozdielom, že Habr berie spätnú väzbu od používateľov oveľa vážnejšie a takéto rozhodnutia zveruje vám ako vývojárom.

Podľa tejto logiky a mojej vlastnej túžby rýchlo vyriešiť problém som zvolil globálne premenné. A ako sa často stáva, skôr či neskôr za ne musíte zaplatiť. Zaplatili sme takmer okamžite: cez víkend sme pracovali, odstraňovali následky, písali posmrtne a začali rozdeľovať server na dve časti. Chyba bola veľmi hlúpa a chybu, ktorá ju zahŕňala, nebolo ľahké reprodukovať. A áno, je to škoda, ale tak či onak, potácajúc sa a stonanie, môj PoC s globálnymi premennými sa napriek tomu dostal do výroby a funguje celkom úspešne, zatiaľ čo čaká na prechod na novú „dvojuzlovú“ architektúru. Bol to dôležitý krok, pretože formálne bol cieľ dosiahnutý - SSR sa naučilo poskytovať úplne pripravenú stránku a používateľské rozhranie sa stalo oveľa pokojnejším.

Protokoly front-end vývojárov Habr: refaktorovanie a reflektovanieRozhranie Mobile Habr po prvej fáze refaktoringu

V konečnom dôsledku architektúra SSR-CSR mobilnej verzie vedie k nasledujúcemu obrázku:

Protokoly front-end vývojárov Habr: refaktorovanie a reflektovanie„Dvojuzlový“ obvod SSR-CSR. Node JS API je vždy pripravené na asynchrónne I/O a nie je blokované funkciou SSR, pretože tá je umiestnená v samostatnej inštancii. Reťazec dopytov č. 3 nie je potrebný.

Odstránenie duplicitných žiadostí

Po vykonaní manipulácií už počiatočné vykreslenie stránky nevyvolávalo epilepsiu. No ďalšie používanie Habra v režime SPA aj tak spôsobilo zmätok.

Keďže základom používateľského toku sú prechody formulára zoznam článkov → článok → komentáre a naopak, v prvom rade bolo dôležité optimalizovať spotrebu zdrojov tohto reťazca.

Protokoly front-end vývojárov Habr: refaktorovanie a reflektovanieNávrat do informačného kanála príspevkov spustí novú požiadavku na údaje

Nebolo treba kopať hlboko. Na screencaste vyššie môžete vidieť, že aplikácia si pri potiahnutí späť vyžiada zoznam článkov a počas požiadavky sa nám články nezobrazujú, čo znamená, že predchádzajúce údaje niekde zmiznú. Zdá sa, že komponent zoznamu článkov používa miestny stav a stratí ho, aby ho zničil. V skutočnosti aplikácia používala globálny stav, ale architektúra Vuex bola postavená priamo na mieste: moduly sú viazané na stránky, ktoré sú zase viazané na trasy. Všetky moduly sú navyše „jednorazové“ – pri každej ďalšej návšteve stránky sa prepíše celý modul:

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

Celkovo sme mali modul Zoznam článkov, ktorý obsahuje objekty typu Článok a modul StranaČlánok, čo bola predĺžená verzia objektu Článok, druh Článok je plný. Celkovo táto implementácia v sebe nenesie nič strašné - je veľmi jednoduchá, dalo by sa dokonca povedať naivná, ale mimoriadne zrozumiteľná. Ak modul resetujete pri každej zmene trasy, môžete s ním dokonca žiť. Napríklad prechod medzi feedmi článkov /kŕmiť → /všetko, zaručene vyhodí všetko, čo sa týka vášho osobného krmiva, keďže máme len jeden Zoznam článkov, do ktorého je potrebné vložiť nové údaje. To nás opäť vedie k duplicite žiadostí.

Po zhromaždení všetkého, čo sa mi podarilo k téme vykopať, som sformuloval novú štátnu štruktúru a predstavil som ju kolegom. Diskusie boli zdĺhavé, ale nakoniec argumenty v prospech prevážili nad pochybnosťami a začal som s realizáciou.

Logika rozhodnutia sa najlepšie odhalí v dvoch fázach. Najprv sa pokúsime oddeliť modul Vuex od stránok a naviazať sa priamo na trasy. Áno, v obchode bude o niečo viac údajov, gettery budú o niečo zložitejšie, ale články nebudeme načítavať dvakrát. Pre mobilnú verziu je to snáď najsilnejší argument. Bude to vyzerať asi takto:

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

Ale čo ak sa zoznamy článkov môžu prekrývať medzi viacerými trasami a čo ak chceme znova použiť dáta objektu Článok vykresliť stránku príspevku a zmeniť ju na Článok je plný? V tomto prípade by bolo logickejšie použiť takúto štruktúru:

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

Zoznam článkov tu je len akési úložisko článkov. Všetky články, ktoré boli stiahnuté počas relácie používateľa. Zaobchádzame s nimi s maximálnou opatrnosťou, pretože ide o premávku, ktorá bola možno načítaná niekde v metre medzi stanicami a túto bolesť používateľovi určite nechceme znova spôsobiť tým, že ho budeme nútiť načítať dáta, ktoré si už stiahol . Objekt ArticlesIds je jednoducho pole ID (napríklad „odkazov“) na objekty Článok. Táto štruktúra vám umožňuje vyhnúť sa duplikácii údajov spoločných pre trasy a opätovnému použitiu objektu Článok pri vykresľovaní stránky príspevku zlúčením rozšírených údajov do nej.

Výstup zoznamu článkov sa tiež stal prehľadnejším: komponent iterátora iteruje cez pole s ID článkov a vykreslí komponent upútavky článku, pričom ID odovzdá ako rekvizitu a podriadený komponent zase získa potrebné údaje z Zoznam článkov. Keď prejdete na stránku publikácie, získame existujúci dátum z Zoznam článkov, požiadame o prijatie chýbajúcich údajov a jednoducho ich doplníme do existujúceho objektu.

Prečo je tento prístup lepší? Ako som písal vyššie, tento prístup je opatrnejší s ohľadom na stiahnuté dáta a umožňuje ich opätovné použitie. Okrem toho však otvára cestu k niektorým novým možnostiam, ktoré dokonale zapadajú do takejto architektúry. Napríklad prieskum a načítanie článkov do informačného kanála tak, ako sa zobrazujú. Najnovšie príspevky môžeme jednoducho uložiť do „úložiska“ Zoznam článkov, uložte samostatný zoznam nových ID do ArticlesIds a upozornite na to používateľa. Keď klikneme na tlačidlo „Zobraziť nové publikácie“, jednoducho vložíme nové ID na začiatok poľa aktuálneho zoznamu článkov a všetko bude fungovať takmer magicky.

Spríjemnenie sťahovania

Čerešničkou na refaktoringovej torte je koncept kostlivcov, vďaka ktorému je proces načítavania obsahu na pomalom internete o niečo menej frustrujúci. O tejto veci sa nediskutovalo, cesta od nápadu k prototypu trvala doslova dve hodiny. Dizajn sa prakticky kreslil sám a naše komponenty sme naučili vykresľovať jednoduché, sotva blikajúce bloky div počas čakania na dáta. Subjektívne tento prístup k zaťaženiu skutočne znižuje množstvo stresových hormónov v tele užívateľa. Kostra vyzerá takto:

Protokoly front-end vývojárov Habr: refaktorovanie a reflektovanie
Habraloading

Reflektovanie

Pracujem v Habre už šesť mesiacov a moji priatelia sa stále pýtajú: no, ako sa vám tam páči? Dobre, pohodlné - áno. Je tu však niečo, čo túto prácu odlišuje od ostatných. Pracoval som v tímoch, ktorým bol ich produkt úplne ľahostajný, nevedeli a nechápali, kto sú ich používatelia. Tu je však všetko inak. Tu cítite zodpovednosť za to, čo robíte. V procese vývoja funkcie sa čiastočne stávate jej vlastníkom, zúčastňujete sa všetkých produktových stretnutí súvisiacich s vašou funkcionalitou, robíte návrhy a sami sa rozhodujete. Vyrobiť produkt, ktorý sami používate každý deň, je veľmi cool a písať kód pre ľudí, ktorí tomu pravdepodobne rozumejú lepšie ako vy, je neuveriteľný pocit (bez sarkazmu).

Po vydaní všetkých týchto zmien sme dostali pozitívnu spätnú väzbu, a to bolo veľmi, veľmi milé. Je to inšpirujúce. Ďakujem! Napíšte viac.

Pripomínam, že po globálnych premenných sme sa rozhodli zmeniť architektúru a alokovať proxy vrstvu do samostatnej inštancie. „Dvojuzlová“ architektúra už dosiahla vydanie vo forme verejného beta testovania. Teraz naň môže prejsť ktokoľvek a pomôcť nám vylepšiť mobilného Habra. To je na dnes všetko. Na všetky vaše otázky rada odpoviem v komentároch.

Zdroj: hab.com

Pridať komentár