Habrov front-end programerski dnevnici: refaktoriranje i refleksija

Habrov front-end programerski dnevnici: refaktoriranje i refleksija

Uvijek me zanimalo kako je Habr strukturiran iznutra, kako je strukturiran tijek rada, kako je strukturirana komunikacija, koji se standardi koriste i kako se ovdje općenito piše kod. Srećom, ukazala mi se takva prilika, jer sam nedavno postao dio habra tima. Na primjeru malog refaktoriranja mobilne verzije pokušat ću odgovoriti na pitanje: kako je raditi ovdje na čelu. U programu: Node, Vue, Vuex i SSR s umakom iz bilješki o osobnom iskustvu u Habru.

Prvo što trebate znati o razvojnom timu je da nas je malo. Nije dovoljno - to su tri fronta, dva beka i tehničko vodstvo svih Habra - Baxley. Tu su, naravno, i tester, dizajner, tri Vadima, čudotvorna metla, marketinški stručnjak i drugi Bumburumi. Ali samo je šest izravnih suradnika Habrovih izvora. To je prilično rijetko - projekt s multimilijunskom publikom, koji izvana izgleda kao divovsko poduzeće, u stvarnosti više izgleda kao ugodan startup s najravnijom mogućom organizacijskom strukturom.

Kao i mnoge druge IT tvrtke, Habr ispovijeda Agile ideje, CI prakse i to je sve. No, prema mojim osjećajima, Habr se kao proizvod razvija više u valovima nego kontinuirano. Dakle, nekoliko sprinteva za redom, marljivo nešto kodiramo, dizajniramo i redizajniramo, nešto lomimo i popravljamo, rješavamo tikete i stvaramo nove, gazimo grablje i pucamo si u noge, da bismo konačno pustili značajku u proizvodnja. A onda dolazi određeno zatišje, razdoblje ponovnog razvoja, vrijeme da se obavi ono što je u kvadrantu "važno-ne hitno".

Upravo o tom “izvansezonskom” sprintu bit će riječi u nastavku. Ovaj put je uključivao refactoring mobilne verzije Habra. Općenito, tvrtka polaže velike nade u njega, au budućnosti bi trebao zamijeniti cijeli zoološki vrt Habrovih inkarnacija i postati univerzalno višeplatformsko rješenje. Jednog dana bit će tu prilagodljivi izgled, PWA, izvanmrežni način rada, prilagodba korisnika i mnoge druge zanimljive stvari.

Postavimo zadatak

Jednom je na običnom stand-upu jedan od čelnika govorio o problemima u arhitekturi komponente komentara mobilne verzije. Imajući to na umu, organizirali smo mikrosastanak u formatu grupne psihoterapije. Svi su redom govorili gdje boli, sve su bilježili na papir, suosjećali, razumjeli, samo što nitko nije pljeskao. Rezultat je bio popis od 20 problema, koji je jasno pokazao da mobilni Habr ima još dug i trnovit put do uspjeha.

Prvenstveno me brinula učinkovitost korištenja resursa i ono što se zove glatko sučelje. Svaki dan, na ruti od kuće do posla i kuće, viđao sam svoj stari telefon kako očajnički pokušava prikazati 20 naslova u feedu. Izgledalo je otprilike ovako:

Habrov front-end programerski dnevnici: refaktoriranje i refleksijaMobile Habr sučelje prije refaktoriranja

Što se ovdje događa? Ukratko, poslužitelj je svima servirao HTML stranicu na isti način, bez obzira je li korisnik bio prijavljen ili ne. Zatim se učitava klijentski JS i ponovno traži potrebne podatke, ali prilagođene za autorizaciju. Odnosno, zapravo smo dva puta radili isti posao. Sučelje je zatreperilo, a korisnik je preuzeo dobrih stotinjak dodatnih kilobajta. U detalje je sve izgledalo još jezivije.

Habrov front-end programerski dnevnici: refaktoriranje i refleksijaStara SSR-CSR shema. Autorizacija je moguća samo u fazama C3 i C4, kada Node JS nije zauzet generiranjem HTML-a i može proxy zahtjeve prema API-ju.

Našu arhitekturu tog vremena vrlo je precizno opisao jedan od korisnika Habra:

Mobilna verzija je sranje. Govorim kako jest. Užasna kombinacija SSR-a i CSR-a.

Morali smo to priznati, koliko god tužno bilo.

Procijenio sam opcije, kreirao kartu u Jiri s opisom na razini "sada je loše, učini to kako treba" i razložio zadatak u širokim crtama:

  • ponovno korištenje podataka,
  • minimizirati broj ponovnih crtanja,
  • uklanjanje dvostrukih zahtjeva,
  • učiniti proces učitavanja očiglednijim.

Ponovno upotrijebimo podatke

U teoriji, prikazivanje na strani poslužitelja osmišljeno je da riješi dva problema: da ne pati od ograničenja tražilice u smislu SPA indeksiranje i poboljšati metriku FMP (neizbježno pogoršanje TTI). U klasičnom scenariju koji konačno formuliran u Airbnbu 2013 godine (još uvijek na Backbone.js), SSR je ista izomorfna JS aplikacija koja radi u okruženju Node. Poslužitelj jednostavno šalje generirani izgled kao odgovor na zahtjev. Zatim dolazi do rehidracije na strani klijenta, a zatim sve radi bez ponovnog učitavanja stranice. Za Habr, kao i za mnoge druge izvore s tekstualnim sadržajem, prikazivanje poslužitelja kritičan je element u izgradnji prijateljskih odnosa s tražilicama.

Unatoč činjenici da je od pojave tehnologije prošlo više od šest godina i da je za to vrijeme u front-end svijetu zaista mnogo vode preteklo ispod mosta, za mnoge developere ova je ideja još uvijek obavijena velom tajne. Nismo stali po strani i pustili smo Vue aplikaciju sa SSR podrškom u produkciju, nedostaje nam jedan mali detalj: nismo poslali početno stanje klijentu.

Zašto? Ne postoji točan odgovor na ovo pitanje. Ili nisu htjeli povećati veličinu odgovora s servera, ili zbog hrpe drugih arhitektonskih problema, ili jednostavno nije išlo. Na ovaj ili onaj način, izbacivanje stanja i ponovno korištenje svega što je poslužitelj učinio čini se prilično prikladnim i korisnim. Zadatak je zapravo trivijalan - stanje se jednostavno ubrizgava u kontekst izvršenja, a Vue ga automatski dodaje u generirani izgled kao globalnu varijablu: window.__INITIAL_STATE__.

Jedan od problema koji se pojavio je nemogućnost pretvaranja cikličkih struktura u JSON (kružna referenca); je riješen jednostavnom zamjenom takvih struktura s njihovim ravnim dvojnicima.

Osim toga, kada radite s UGC sadržajem, trebali biste zapamtiti da se podaci trebaju pretvoriti u HTML entitete kako se ne bi pokvario HTML. U te svrhe koristimo he.

Minimiziranje precrtavanja

Kao što možete vidjeti na gornjem dijagramu, u našem slučaju, jedna instanca Node JS obavlja dvije funkcije: SSR i "proxy" u API-ju, gdje se događa autorizacija korisnika. Ova okolnost onemogućuje autorizaciju dok se JS kod izvodi na poslužitelju, budući da je čvor jednonitni, a SSR funkcija je sinkrona. To jest, poslužitelj jednostavno ne može slati zahtjeve samom sebi dok je pozivni skup nečim zauzet. Ispostavilo se da smo ažurirali stanje, ali sučelje nije prestalo trzati, jer su podaci o klijentu morali biti ažurirani uzimajući u obzir korisničku sesiju. Trebali smo naučiti našu aplikaciju da stavi točne podatke u početno stanje, uzimajući u obzir prijavu korisnika.

Postojala su samo dva rješenja problema:

  • priložiti autorizacijske podatke zahtjevima između poslužitelja;
  • podijelite slojeve Node JS u dvije odvojene instance.

Prvo rješenje je zahtijevalo korištenje globalnih varijabli na serveru, a drugo produžilo rok za izvršenje zadatka za najmanje mjesec dana.

Kako napraviti izbor? Habr se često kreće putem manjeg otpora. Neformalno, postoji opća želja da se ciklus od ideje do prototipa svede na minimum. Model odnosa prema proizvodu pomalo podsjeća na postulate booking.com-a, s jedinom razlikom što Habr puno ozbiljnije shvaća povratne informacije korisnika i vjeruje vama kao developeru u donošenju takvih odluka.

Slijedeći tu logiku i vlastitu želju za brzim rješavanjem problema, odabrao sam globalne varijable. I, kao što se često događa, morate platiti za njih prije ili kasnije. Platili smo gotovo odmah: radili smo vikendom, čistili posljedice, pisali obdukcija i počeo dijeliti poslužitelj na dva dijela. Greška je bila vrlo glupa, a bug koji je uključivao nije bilo lako reproducirati. I da, šteta za ovo, ali ovako ili onako, posrćući i stenjući, moj PoC s globalnim varijablama ipak je krenuo u proizvodnju i prilično uspješno radi dok čeka prelazak na novu arhitekturu s dva čvora. Ovo je bio važan korak, jer je formalno cilj postignut - SSR je naučio isporučiti stranicu potpuno spremnu za korištenje, a korisničko sučelje postalo je puno mirnije.

Habrov front-end programerski dnevnici: refaktoriranje i refleksijaMobile Habr sučelje nakon prve faze refaktoriranja

U konačnici, SSR-CSR arhitektura mobilne verzije vodi do ove slike:

Habrov front-end programerski dnevnici: refaktoriranje i refleksijaSSR-CSR sklop “dva čvora”. Node JS API uvijek je spreman za asinkroni I/O i ne blokira ga SSR funkcija, jer se potonja nalazi u zasebnoj instanci. Lanac upita #3 nije potreban.

Uklanjanje dvostrukih zahtjeva

Nakon izvršenih manipulacija, početno renderiranje stranice više nije izazivalo epilepsiju. Ali daljnja upotreba Habr-a u SPA modu i dalje je izazivala zabunu.

Budući da su temelj korisničkog toka prijelazi forme popis članaka → članak → komentari i obrnuto, bilo je važno optimizirati potrošnju resursa u ovom lancu na prvom mjestu.

Habrov front-end programerski dnevnici: refaktoriranje i refleksijaPovratak na post feed izaziva novi zahtjev za podacima

Nije bilo potrebe kopati duboko. Na screencastu iznad možete vidjeti da aplikacija ponovno traži popis članaka kada prijeđete natrag, a tijekom zahtjeva ne vidimo članke, što znači da prethodni podaci negdje nestaju. Čini se da komponenta popisa članaka koristi lokalno stanje i gubi ga nakon uništavanja. Zapravo, aplikacija je koristila globalno stanje, ali je Vuex arhitektura izgrađena direktno: moduli su vezani za stranice, koje su zauzvrat vezane za rute. Štoviše, svi moduli su "za jednokratnu upotrebu" - svaki sljedeći posjet stranici prepisao je cijeli modul:

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

Ukupno smo imali modul Popis članaka, koji sadrži objekte tipa Članak i modul StranicaArticle, koji je bio proširena verzija objekta Članak, nekako Članak pun. Općenito, ova implementacija sama po sebi ne nosi ništa strašno - vrlo je jednostavna, moglo bi se čak reći naivna, ali vrlo razumljiva. Ako resetirate modul svaki put kada promijenite rutu, onda možete čak i živjeti s tim. Međutim, na primjer, kretanje između feedova članaka /feed → /sve, zajamčeno će odbaciti sve vezano uz osobni feed, budući da imamo samo jedan Popis članaka, u koji trebate unijeti nove podatke. To nas opet dovodi do dupliciranja zahtjeva.

Nakon što sam prikupio sve što sam uspio iskopati o toj temi, formulirao sam novi državni ustroj i predstavio ga svojim kolegama. Rasprave su bile dugotrajne, ali na kraju su argumenti za prevagnuli nad sumnjama i ja sam krenuo u realizaciju.

Logika rješenja najbolje se otkriva u dva koraka. Prvo pokušavamo odvojiti Vuex modul od stranica i vezati ga izravno na rute. Da, bit će malo više podataka u trgovini, geteri će postati malo složeniji, ali nećemo učitavati artikle dvaput. Za mobilnu verziju ovo je možda najjači argument. Izgledat će otprilike ovako:

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

Ali što ako se popisi članaka mogu preklapati između više ruta i što ako želimo ponovno upotrijebiti podatke o objektima Članak za prikaz stranice objave, pretvarajući je u Članak pun? U ovom slučaju bilo bi logičnije koristiti takvu strukturu:

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

Popis članaka ovdje je to samo neka vrsta repozitorija članaka. Svi članci koji su preuzeti tijekom korisničke sesije. S njima se odnosimo s najvećom pažnjom, jer je to promet koji je možda preuzet kroz pain negdje u metrou između stanica, i definitivno ne želimo korisniku ponovno prouzročiti tu muku tjerajući ga da učitava podatke koje je već imao preuzeto. Objekt Id-ovi članaka je jednostavno niz ID-ova (kao da su "veze") na objekte Članak. Ova struktura vam omogućuje da izbjegnete dupliciranje podataka uobičajenih za rute i ponovnu upotrebu objekta Članak prilikom renderiranja stranice objave spajanjem proširenih podataka u nju.

Izlaz popisa članaka također je postao transparentniji: komponenta iteratora iterira kroz niz s ID-ovima članaka i crta komponentu najave članka, prosljeđujući ID kao potporu, a komponenta dijete zauzvrat dohvaća potrebne podatke iz Popis članaka. Kada odete na stranicu objave, dobivamo već postojeći datum od Popis članaka, postavljamo zahtjev za dobivanje podataka koji nedostaju i jednostavno ih dodajemo postojećem objektu.

Zašto je ovaj pristup bolji? Kao što sam gore napisao, ovaj pristup je nježniji u odnosu na preuzete podatke i omogućuje vam da ih ponovno koristite. No, osim toga, otvara put nekim novim mogućnostima koje se savršeno uklapaju u takvu arhitekturu. Na primjer, anketiranje i učitavanje članaka u feed čim se pojave. Najnovije postove jednostavno možemo staviti u "skladište" Popis članaka, spremite zaseban popis novih ID-ova u Id-ovi članaka i o tome obavijestiti korisnika. Kada kliknemo na gumb “Prikaži nove publikacije”, jednostavno ćemo umetnuti nove ID-ove na početak niza trenutnog popisa članaka i sve će raditi gotovo čarobno.

Učiniti preuzimanje ugodnijim

Šlag na torti refactoringa je koncept kostura, koji proces preuzimanja sadržaja na sporom Internetu čini malo manje odvratnim. O tome nije bilo nikakvih rasprava, put od ideje do prototipa trajao je doslovno dva sata. Dizajn se praktički sam nacrtao, a naše smo komponente naučili da renderiraju jednostavne, jedva treperave div blokove dok čekaju podatke. Subjektivno, ovakav pristup opterećenju zapravo smanjuje količinu hormona stresa u tijelu korisnika. Kostur izgleda ovako:

Habrov front-end programerski dnevnici: refaktoriranje i refleksija
Habraloading

Razmišljajući

Radim u Habréu šest mjeseci i moji prijatelji i dalje pitaju: dobro, kako ti se sviđa tamo? Dobro, udobno - da. Ali postoji nešto što ovaj rad čini drugačijim od drugih. Radio sam u timovima koji su bili potpuno ravnodušni prema svom proizvodu, nisu znali niti razumjeli tko su njihovi korisnici. Ali ovdje je sve drugačije. Ovdje se osjećate odgovornim za ono što radite. U procesu razvoja značajke, djelomično postajete njen vlasnik, sudjelujete na svim sastancima o proizvodu vezanim uz vašu funkcionalnost, dajete prijedloge i sami donosite odluke. Napraviti proizvod koji sami koristite svaki dan je jako cool, ali pisanje koda za ljude koji su vjerojatno bolji u tome od vas jednostavno je nevjerojatan osjećaj (bez sarkazma).

Nakon objavljivanja svih ovih promjena, dobili smo pozitivne povratne informacije, i bilo je jako, jako lijepo. To je inspirativno. Hvala vam! Napiši više.

Dopustite da vas podsjetim da smo nakon globalnih varijabli odlučili promijeniti arhitekturu i alocirati proxy sloj u zasebnu instancu. Arhitektura "dva čvora" već je stigla do objave u obliku javnog beta testiranja. Sada se svatko može prebaciti na njega i pomoći nam da poboljšamo mobilni Habr. To je sve za danas. Rado ću odgovoriti na sva vaša pitanja u komentarima.

Izvor: www.habr.com

Dodajte komentar