Dnevnici Habr front-end programera: refaktoring i refleksija

Dnevnici Habr front-end programera: refaktoring i refleksija

Oduvijek me je zanimalo kako je Habr strukturiran iznutra, kako je struktuiran radni tok, kako su strukturirane komunikacije, koji se standardi koriste i kako je kod uopće napisan ovdje. Na svu sreću, dobio sam ovu priliku, jer sam nedavno postao dio habra tima. Na primjeru malog refaktoriranja mobilne verzije pokušat ću odgovoriti na pitanje: kako je ovdje raditi na prvoj liniji? U programu: Node, Vue, Vuex i SSR sa sosom iz bilješki o ličnom iskustvu u Habru.

Prva stvar koju trebate znati o razvojnom timu je da smo mali. Malo je tri fronta, dva beka i tehničko vodstvo svih Habr - Baxley. Tu su, naravno, i tester, dizajner, tri Vadima, čudotvorna metla, marketer i ostali Bumburumi. Ali postoji samo šest direktnih saradnika Habrovih stranica. Ovo je prilično rijetko - projekt sa višemilionskom publikom, koji spolja izgleda kao gigantski poduhvat, zapravo je više kao ugodan startup sa najravnijom organizacijskom strukturom.

Kao i mnoge druge IT kompanije, Habr ispovijeda Agile ideje, CI prakse, i to je sve. Ali po mom mišljenju, Habr se kao proizvod razvija u valovima, a ne kontinuirano. Dakle, nekoliko sprinteva zaredom marljivo nešto kodiramo, dizajniramo i redizajniramo, nešto pokvarimo i popravimo, rješavamo tikete i stvaramo nove, stanemo na grablje i pucamo sebi u noge kako bismo konačno pustili funkciju u budućnost. A onda dolazi određeno zatišje, period ponovnog razvoja, vrijeme da se uradi ono što je u kvadrantu „važno-ne hitno“.

Upravo o ovom “vansezonskom” sprintu će biti riječi u nastavku. Ovaj put je uključivao refaktoriranje mobilne verzije Habra. Generalno, kompanija u to polaže velike nade, au budućnosti bi trebao zamijeniti cijeli zoološki vrt Habrovih inkarnacija i postati univerzalno cross-platform rješenje. Jednog dana će postojati prilagodljivi izgled, PWA, offline način rada, prilagođavanje korisnika i mnoge druge zanimljive stvari.

Postavimo zadatak

Jednom, na običnom stand-upu, jedan od prednjih je govorio o problemima u arhitekturi komponente komentara mobilne verzije. Na ovaj prijedlog organizirali smo mikro-sastanak u formatu grupne psihoterapije. Svi su naizmjenično govorili gdje boli, sve su snimali na papir, saosjećali, razumjeli, samo što niko nije pljeskao. Rezultat je bila lista od 20 problema, što je jasno dalo do znanja da mobilni Habr još uvijek ima dug i trnovit put do uspjeha.

Prije svega sam bio zabrinut za efikasnost resursa i ono što se zove glatki interfejs. Svakog dana, na putu „kuća-posao-kuća“, viđao sam svoj stari telefon kako očajnički pokušava da prikaže 20 naslova u feedu. Izgledalo je otprilike ovako:

Dnevnici Habr front-end programera: refaktoring i refleksijaMobilni Habr interfejs prije refaktoriranja

sta se desava ovde? Ukratko, server je servirao HTML stranicu svima na isti način, bez obzira da li je korisnik prijavljen ili ne. Zatim se klijentski JS učitava i ponovo traži potrebne podatke, ali ovaj put prilagođen za autorizaciju. Odnosno, mi smo zapravo dva puta radili isti posao. Interfejs je zatreperio, a korisnik je preuzeo dobrih stotinu dodatnih kilobajta. U detaljima je sve izgledalo još jezivije.

Dnevnici Habr front-end programera: refaktoring i refleksijaStara šema SSR-CSR. Autorizacija je moguća samo u fazama C3 i C4, kada Node JS nije zauzet generiranjem HTML-a i može proxy zahtjeve API-ju.

Našu tadašnju arhitekturu je vrlo precizno opisao jedan od korisnika Habra:

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

Bili smo primorani da to priznamo, ma koliko tužno bilo.

Procijenio sam opcije, kreirao tiket u Jira s opisom na nivou „sada je loše, uradi to u redu“ i razložio zadatak u širokim crtama:

  • ponovo koristiti podatke,
  • minimizirati broj ponovnih crtanja,
  • eliminirati duple zahtjeve,
  • učinite proces učitavanja očiglednijim.

Hajde da ponovo upotrebimo podatke

U teoriji, renderiranje na strani servera je dizajnirano da riješi dva problema: da ne pati od ograničenja pretraživača u smislu SPA indeksiranje i poboljšati metriku FMP (neizbežno pogoršanje TTI). U klasičnom scenariju to konačno formulisano na Airbnb-u 2013 godine (nazad na Backbone.js), SSR je ista izomorfna JS aplikacija koja radi u Node okruženju. Server jednostavno vraća generirani izgled kao odgovor na zahtjev. Zatim dolazi do rehidracije na strani klijenta i tada sve radi bez ponovnog učitavanja stranice. Za Habr, kao i za mnoge druge resurse sa tekstualnim sadržajem, serversko prikazivanje je kritičan element u izgradnji prijateljskih odnosa sa pretraživačima.

Unatoč činjenici da je prošlo više od šest godina otkako se tehnologija pojavila, a za to vrijeme je puno vode prošlo ispod mosta u front-end svijetu, za mnoge programere ova ideja je još uvijek obavijena velom tajne. Nismo ostali po strani i u produkciju smo uveli Vue aplikaciju sa podrškom za SSR, pri čemu nedostaje jedan mali detalj: nismo prenijeli početno stanje klijentu.

Zašto? Ne postoji tačan odgovor na ovo pitanje. Ili nisu željeli povećati veličinu odgovora sa servera, ili zbog gomile drugih arhitektonskih problema, ili jednostavno nije krenulo. Na ovaj ili onaj način, izbacivanje stanja i ponovno korištenje svega što je server uradio izgleda sasvim prikladno i korisno. Zadatak je zapravo trivijalan. stanje se jednostavno ubrizgava u kontekst izvršavanja, 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šeno jednostavnom zamjenom takvih konstrukcija njihovim ravnim kolegama.

Pored toga, kada se bavite UGC sadržajem, treba da zapamtite da podatke treba konvertovati u HTML entitete kako se ne bi razbio HTML. Za ove svrhe koristimo he.

Minimiziranje ponovnog crtanja

Kao što možete vidjeti iz gornjeg dijagrama, u našem slučaju, jedna Node JS instanca obavlja dvije funkcije: SSR i “proxy” u API-ju, gdje dolazi do autorizacije korisnika. Ova okolnost onemogućava autorizaciju dok JS kod radi na serveru, budući da je čvor jednonitni, a SSR funkcija je sinhrona. To jest, server jednostavno ne može sam sebi slati zahtjeve dok je stek poziva zauzet nečim. Ispostavilo se da smo ažurirali stanje, ali interfejs nije prestao da se trza, jer su podaci o klijentu morali da se ažuriraju uzimajući u obzir korisničku sesiju. Bilo je potrebno naučiti našu aplikaciju da stavi ispravne podatke u početno stanje, uzimajući u obzir prijavu korisnika.

Postojala su samo dva rješenja problema:

  • povežite podatke o autorizaciji sa zahtjevima na više servera;
  • podijeliti slojeve Node JS u dvije odvojene instance.

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

Kako napraviti izbor? Habr se često kreće putem najmanjeg otpora. Neformalno, postoji opšta želja da se ciklus od ideje do prototipa svede na minimum. Model odnosa prema proizvodu donekle podsjeća na postulate booking.com, s jedinom razlikom što Habr mnogo ozbiljnije shvaća povratne informacije korisnika i takve odluke povjerava vama kao programeru.

Slijedeći ovu logiku i vlastitu želju da brzo riješim problem, odabrao sam globalne varijable. I, kao što se često dešava, morate ih platiti prije ili kasnije. Platili smo skoro odmah: radili smo vikendom, raščistili posledice, pisali postmortem i počeo da deli server na dva dela. Greška je bila veoma glupa, a grešku koja je uključivala nije bilo lako reprodukovati. I da, šteta za ovo, ali na ovaj ili onaj način, posrćući i stenjajući, moj PoC sa globalnim varijablama je ipak ušao u proizvodnju i radi prilično uspješno dok čeka prelazak na novu arhitekturu s dva čvora. Ovo je bio važan korak, jer je formalno cilj postignut - SSR je naučio da isporučuje potpuno spremnu stranicu, a korisničko sučelje je postalo mnogo smirenije.

Dnevnici Habr front-end programera: refaktoring i refleksijaMobilni Habr interfejs nakon prve faze refaktoriranja

Konačno, SSR-CSR arhitektura mobilne verzije dovodi do sljedeće slike:

Dnevnici Habr front-end programera: refaktoring i refleksijaSSR-CSR kolo “dva čvora”. Node JS API je uvijek spreman za asinhroni I/O i nije blokiran od strane SSR funkcije, budući da se potonja nalazi u zasebnoj instanci. Lanac upita #3 nije potreban.

Uklanjanje duplih zahtjeva

Nakon izvršenih manipulacija, početni prikaz stranice više nije izazivao epilepsiju. Ali daljnja upotreba Habra u SPA modu je ipak izazvala zabunu.

Budući da je osnova korisničkog toka prijelazi forme lista članaka → članak → komentari i obrnuto, bilo je važno optimizirati potrošnju resursa ovog lanca na prvom mjestu.

Dnevnici Habr front-end programera: refaktoring i refleksijaPovratak na objavu sadržaja pokreće novi zahtjev za podacima

Nije bilo potrebe da se kopa duboko. U screencast-u iznad možete vidjeti da aplikacija ponovo traži listu članaka kada prevučete unazad, a tokom zahtjeva ne vidimo članke, što znači da prethodni podaci negdje nestaju. Izgleda da komponenta liste članaka koristi lokalno stanje i gubi ga da bi ga uništila. U stvari, aplikacija je koristila globalno stanje, ali Vuex arhitektura je izgrađena direktno: moduli su vezani za stranice, koje su zauzvrat vezane za rute. Štaviše, svi moduli su "jednokratni" - svaka naredna posjeta stranici prepisala je cijeli modul:

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

Ukupno smo imali modul ArticlesList, koji sadrži objekte tipa članak i modul PageArticle, što je bila proširena verzija objekta članak, vrsta ArticleFull. Uglavnom, ova implementacija sama po sebi ne nosi ništa strašno - vrlo je jednostavna, moglo bi se reći i naivna, ali krajnje razumljiva. Ako resetujete modul svaki put kada promijenite rutu, onda možete čak i živjeti s njim. Međutim, prijelaz između feedova članaka, na primjer /feed → /sve, garantovano će baciti sve što je u vezi sa vašim ličnim feedom, jer imamo samo jedan ArticlesList, u koji treba da unesete nove podatke. To nas opet dovodi do dupliranja zahtjeva.

Sakupivši sve što sam uspio iskopati na tu temu, formulirao sam novu državnu strukturu i predstavio je svojim kolegama. Diskusije su bile duge, ali su na kraju argumenti u prilog nadmašili sumnje i ja sam počeo sa implementacijom.

Logika odluke najbolje se otkriva u dvije faze. Prvo pokušavamo da odvojimo Vuex modul od stranica i direktno se povežemo sa rutama. Da, bit će malo više podataka u trgovini, getteri će postati malo složeniji, ali nećemo učitavati članke dvaput. Za mobilnu verziju, ovo je možda najjači argument. To će izgledati otprilike ovako:

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

Ali šta ako se liste članaka mogu preklapati između više ruta i šta ako želimo ponovo koristiti podatke objekta članak da prikažete stranicu posta, pretvarajući je u ArticleFull? U ovom slučaju bi bilo logičnije koristiti takvu strukturu:

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

ArticlesList ovdje je samo neka vrsta spremišta članaka. Svi članci koji su preuzeti tokom korisničke sesije. Prema njima postupamo s najvećom pažnjom, jer se radi o prometu koji je možda bio učitan negdje u metrou između stanica, i definitivno ne želimo ponovo nanijeti ovu bol korisniku prisiljavajući ga da učitava podatke koje je već preuzeo . Objekt ArticlesIds je jednostavno niz ID-ova (poput "linkova") prema objektima članak. Ova struktura vam omogućava da izbjegnete dupliciranje podataka uobičajenih za rute i ponovnu upotrebu objekta članak prilikom prikazivanja stranice posta spajanjem proširenih podataka u nju.

Izlaz liste članaka je također postao transparentniji: komponenta iteratora iterira niz s ID-ovima članaka i crta komponentu teaser-a članka, prosljeđujući ID kao prop, a podređena komponenta, zauzvrat, preuzima potrebne podatke iz ArticlesList. Kada odete na stranicu publikacije, preuzimamo postojeći datum od ArticlesList, postavljamo zahtjev za primanje podataka koji nedostaju i jednostavno ih dodajemo postojećem objektu.

Zašto je ovaj pristup bolji? Kao što sam gore napisao, ovaj pristup je pažljiviji u odnosu na preuzete podatke i omogućava vam da ih ponovo koristite. Ali osim toga, otvara put nekim novim mogućnostima koje se savršeno uklapaju u takvu arhitekturu. Na primjer, ispitivanje i učitavanje članaka u feed kako se pojavljuju. Možemo jednostavno staviti najnovije postove u "skladište" ArticlesList, sačuvajte posebnu listu novih ID-ova u ArticlesIds i o tome obavijestiti korisnika. Kada kliknemo na dugme “Prikaži nove publikacije”, jednostavno ćemo umetnuti nove ID-ove na početak niza trenutne liste članaka i sve će raditi gotovo magično.

Učinite preuzimanje ugodnijim

Šlag na torti refaktoringa je koncept kostura, koji proces učitavanja sadržaja na sporom internetu čini malo manje frustrirajućim. Nije bilo razgovora o ovom pitanju, put od ideje do prototipa trajao je bukvalno dva sata. Dizajn se praktički iscrtao, a mi smo naučili naše komponente da renderiraju jednostavne, jedva trepereće div blokove dok čekaju podatke. Subjektivno, ovaj pristup učitavanju zapravo smanjuje količinu hormona stresa u tijelu korisnika. Kostur izgleda ovako:

Dnevnici Habr front-end programera: refaktoring i refleksija
Habraloading

Reflecting

Radim u Habru šest mjeseci i moji prijatelji i dalje pitaju: pa kako ti se sviđa tamo? U redu, udobno - da. Ali postoji nešto što ovaj posao izdvaja od drugih. Radio sam u timovima koji su bili potpuno ravnodušni prema svom proizvodu, koji nisu znali niti razumjeli ko su njihovi korisnici. Ali ovdje je sve drugačije. Ovdje se osjećate odgovornim za ono što radite. U procesu razvoja neke funkcije, dijelom postajete njen vlasnik, učestvujete na svim sastancima proizvoda vezanim za vašu funkcionalnost, sami dajete prijedloge i donosite odluke. Stvaranje proizvoda koji sami koristite svaki dan je jako cool, a pisanje koda za ljude koji ga vjerovatno razumiju bolje od vas je jednostavno nevjerovatan osjećaj (bez sarkazma).

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

Da vas podsjetim da smo nakon globalnih varijabli odlučili promijeniti arhitekturu i dodijeliti proxy sloj u zasebnu instancu. Arhitektura "dva čvora" je već stigla do objavljivanja u obliku javnog beta testiranja. Sada se svako 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