Logs di sviluppatori front-end Habr: refactoring è riflessione

Logs di sviluppatori front-end Habr: refactoring è riflessione

Aghju sempre interessatu in quantu Habr hè strutturatu da l'internu, cumu u flussu di travagliu hè strutturatu, cumu e cumunicazioni sò strutturati, chì standard sò usati è cumu u codice hè generalmente scrittu quì. Fortunatamente, aghju avutu una tale opportunità, perchè recentemente aghju diventatu parte di a squadra habra. Aduprendu l'esempiu di una piccula refactoring di a versione mobile, pruvaraghju à risponde à a quistione: cumu hè di travaglià quì in fronte. In u prugramma: Node, Vue, Vuex è SSR cù salsa da note nantu à l'esperienza persunale in Habr.

A prima cosa chì avete bisognu di sapè nantu à a squadra di sviluppu hè chì ci sò pochi di noi. Micca abbastanza - quessi sò trè fronti, dui backs è u capu tecnicu di tutti Habr - Baxley. Ci hè, sicuru, ancu un tester, un designer, trè Vadim, una scupa miraculosa, un specialista di marketing è altri Bumburums. Ma ci sò solu sei cuntributori diretti à e fonti di Habr. Questu hè abbastanza raru - un prughjettu cù una audienza multimilionaria, chì da l'esternu s'assumiglia à una impresa gigante, in realtà s'assumiglia più à una startup accogliente cù a struttura organizzativa più plana pussibule.

Cum'è parechje altre cumpagnie IT, Habr professa idee Agile, pratiche CI, è questu hè tuttu. Ma sicondu i mo sentimenti, Habr cum'è un pruduttu si sviluppa più in ondate chè continuamente. Dunque, per parechji sprints in una fila, codificamu diligentemente qualcosa, cuncepemu è ridisegnate, rompemu qualcosa è riparate, risolvemu i biglietti è creanu novi, mettemu in un rake è sparemu in i pedi, per finalmente liberà a funzione in pruduzzione. E poi vene una certa calma, un periudu di ristrutturazione, u tempu di fà ciò chì hè in u quadrante "impurtante-micca urgente".

Hè precisamente stu sprint "off-season" chì serà discututu quì sottu. Questa volta hà inclusu un refactoring di a versione mobile di Habr. In generale, a cumpagnia hà grandi speranze per questu, è in u futuru deve rimpiazzà tuttu u zoo di l'incarnazioni di Habr è diventà una soluzione universale multiplataforma. Qualchì ghjornu ci sarà layout adattativu, PWA, modalità offline, persunalizazione di l'utilizatori, è parechje altre cose interessanti.

Fighjemu u compitu

Una volta, in un stand-up ordinariu, unu di i fronti hà parlatu di prublemi in l'architettura di u cumpunente di cumenti di a versione mobile. Cù questu in mente, avemu urganizatu una micro-reunione in u formatu di psicoterapia di gruppu. Ognunu si turnava dicendu induv’ellu duria, arregistravanu tuttu nant’à carta, si simpatizavanu, si capiscenu, salvu chì nimu applaude. U risultatu era una lista di 20 prublemi, chì hà fattu chjaru chì Habr mobile avia sempre una strada longa è spinosa per u successu.

Eru primuramente preoccupatu per l'efficienza di l'usu di e risorse è ciò chì hè chjamatu una interfaccia liscia. Ogni ghjornu, nantu à a strada casa-travagliu-casa, aghju vistu u mo vechju telefunu chì prova disperatamente di vede 20 tituli in u feed. Paria qualcosa cusì:

Logs di sviluppatori front-end Habr: refactoring è riflessioneInterfaccia Mobile Habr prima di refactoring

Chì si passa quì ? In corta, u servitore hà servitu a pagina HTML à tutti in u listessu modu, indipendentemente da chì l'utilizatore era logatu o micca. Allora u cliente JS hè caricatu è dumanda di novu i dati necessarii, ma aghjustatu per l'autorizazione. Vale à dì, avemu fattu u listessu travagliu duie volte. L'interfaccia lampò, è l'utilizatore hà scaricatu un bonu centu kilobyte extra. In dettagliu tuttu pareva ancu più spaventoso.

Logs di sviluppatori front-end Habr: refactoring è riflessioneVechju schema SSR-CSR. L'autorizazione hè pussibule solu in e tappe C3 è C4, quandu Node JS ùn hè micca occupatu à generà HTML è pò proxy richieste à l'API.

A nostra architettura di quellu tempu hè stata descritta assai accuratamente da unu di l'utilizatori di Habr:

A versione mobile hè una merda. A dicu cum'ellu hè. Una terribile combinazione di SSR è CSR.

Avemu avutu à ammette, ùn importa micca quantu era tristu.

Aghju valutatu l'opzioni, aghju creatu un bigliettu in Jira cù una descrizzione à u livellu di "hè male avà, fate bè" è hà decompostu u compitu in grandi colpi:

  • riutilizà dati,
  • minimizzà u numeru di redraws,
  • eliminà e dumande duplicate,
  • rende u prucessu di carica più evidenti.

Riutilicemu i dati

In teoria, u rendering di u servitore hè pensatu per risolve dui prublemi: ùn soffre micca di limitazioni di u mutore di ricerca in quantu à Indexation SPA è migliurà a metrica FMP (inevitabilmente peggioramento TTI). In un scenariu classicu chì finalmente formulatu in Airbnb in 2013 annu (ancora nantu à Backbone.js), SSR hè a listessa applicazione JS isomorfica in esecuzione in l'ambiente Node. U servitore simpricimenti manda u layout generatu cum'è una risposta à a dumanda. Allora a reidratazione si trova in u latu di u cliente, è dopu tuttu funziona senza ricaricà di pagina. Per Habr, cum'è per parechje altre risorse cù u cuntenutu di testu, u rendering di u servitore hè un elementu criticu in a creazione di relazioni amichevuli cù i mutori di ricerca.

Malgradu u fattu chì più di sei anni sò passati da l'avventu di a tecnulugia, è in questu tempu assai acqua hà veramente volatu sottu à u ponte in u mondu di front-end, per parechji sviluppatori sta idea hè sempre in u sicretu. Ùn avemu micca staccatu è lanciatu una applicazione Vue cù supportu SSR à a produzzione, mancava un picculu dettagliu: ùn avemu micca mandatu u statu iniziale à u cliente.

Perchè? Ùn ci hè micca risposta precisa à sta quistione. O ùn vulianu micca aumentà a dimensione di a risposta da u servitore, o per via di una mansa d'altri prublemi architettonici, o simpricimenti ùn hà micca pigliatu. In un modu o l'altru, scaccià u statu è riutilizà tuttu ciò chì u servitore hà fattu pare abbastanza appropritatu è utile. U compitu hè veramente triviale - u statu hè solu injected in u cuntestu di esecutivu, è Vue l'aghjunghje automaticamente à u layout generatu cum'è una variabile globale: window.__INITIAL_STATE__.

Unu di i prublemi chì hè ghjuntu hè l'incapacità di cunvertisce strutture cicliche in JSON (riferenza circulare); hè stata risolta da solu rimpiazzà tali strutture cù i so contraparti flat.

Inoltre, quandu si tratta di cuntenutu UGC, avete da ricurdà chì e dati deve esse cunvertiti in entità HTML per ùn rompe micca l'HTML. Per questi scopi avemu aduprà he.

Minimizà i redraws

Comu pudete vede da u diagramma sopra, in u nostru casu, una istanza di Node JS eseguisce duie funzioni: SSR è "proxy" in l'API, induve l'autorizazione di l'utilizatori si trova. Questa circustanza rende impussibile d'autorizà mentre u codice JS hè in esecuzione nantu à u servitore, postu chì u node hè unicu filatu, è a funzione SSR hè sincrona. Questu hè, u servitore simpricimenti ùn pò micca mandà richieste à ellu stessu mentre u callstack hè occupatu cù qualcosa. Ci hè statu chì avemu aghjurnatu u statu, ma l'interfaccia ùn hà micca cessatu di twitching, postu chì e dati nantu à u cliente anu da esse aghjurnatu tenendu in contu a sessione di l'utilizatori. Avemu bisognu di insignà a nostra applicazione per mette i dati curretti in u statu iniziale, tenendu in contu u login di l'utilizatore.

Ci era solu duie suluzioni à u prublema:

  • attaccà i dati d'autorizazione à e dumande cross-server;
  • split Node JS strati in dui casi separati.

A prima suluzione necessitava l'usu di variàbili glubale nantu à u servitore, è a seconda allargava u termini per cumpiendu u compitu da almenu un mesi.

Cumu fà una scelta? Habr spessu si move nantu à a strada di a minima resistenza. Informalmente, ci hè un desideriu generale di riduce u ciculu da l'idea à u prototipu à u minimu. U mudellu di l'attitudine versu u pruduttu hè un pocu di reminiscenza di i postulati di booking.com, cù l'unica diferenza hè chì Habr piglia assai più seriu u feedback di l'utilizatori è fiducia in voi, cum'è sviluppatore, per piglià tali decisioni.

Dopu sta logica è u mo propiu desideriu di risolve rapidamente u prublema, aghju sceltu variàbili globale. E, cum'è spessu succede, avete da pagà per elli prima o dopu. Avemu pagatu quasi subitu: avemu travagliatu u weekend, sbulicatu e cunsequenze, hà scrittu post-mortem è hà cuminciatu à dividisce u servitore in dui parti. L'errore era assai stupidu, è u bug chì implicava ùn era micca faciule di ripruduce. È iè, hè una vergogna per questu, ma in una manera o in l'altru, stumbling and groaning, u mo PoC cù variabili glubale hè ancu andatu in pruduzzione è travaglia abbastanza bè mentre aspittendu u muvimentu à una nova architettura di "dui nodi". Questu era un passu impurtante, perchè formalmente u scopu hè statu rializatu - SSR hà amparatu à furnisce una pagina cumpletamente pronta per l'usu, è l'UI hè diventatu assai più tranquillu.

Logs di sviluppatori front-end Habr: refactoring è riflessioneInterfaccia Mobile Habr dopu à a prima tappa di refactoring

In ultimamente, l'architettura SSR-CSR di a versione mobile porta à sta stampa:

Logs di sviluppatori front-end Habr: refactoring è riflessioneCircuitu SSR-CSR à "dui nodi". L'API Node JS hè sempre pronta per l'I / O asincrona è ùn hè micca bluccata da a funzione SSR, postu chì l'ultima si trova in una istanza separata. A catena di query #3 ùn hè micca necessariu.

Eliminazione di e dumande duplicate

Dopu chì e manipulazioni sò state realizate, a prestazione iniziale di a pagina ùn hà più pruvucatu epilepsia. Ma l'usu ulteriore di Habr in u modu SPA hà ancu causatu cunfusione.

Siccomu a basa di u flussu di l'utilizatori hè transizioni di a forma lista di articuli → articulu → cumenti è vice versa, era impurtante per ottimisimu u cunsumu di risorse di sta catena in u primu locu.

Logs di sviluppatori front-end Habr: refactoring è riflessioneRiturnà à u post feed pruvuca una nova dumanda di dati

Ùn ci era micca bisognu di scavà in fondu. In u screencast sopra, pudete vede chì l'appiecazione torna à dumandà a lista di l'articuli quandu scorri in daretu, è durante a dumanda ùn vedemu micca l'articuli, chì significa chì i dati previ spariscenu in qualchì locu. Sembra chì u cumpunente di a lista di l'articuli usa un statu lucale è u perde nantu à a distruzzione. In fatti, l'appiecazione hà utilizatu un statu globale, ma l'architettura Vuex hè stata custruita in capu: i moduli sò ligati à e pagine, chì à u turnu sò ligati à e rotte. Inoltre, tutti i moduli sò "disponibili" - ogni visita successiva à a pagina riscrivia u modulu sanu:

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

In totale, avemu avutu un modulu Lista di l'articuli, chì cuntene oggetti di tipu articulu è modulu PaginaArticulu, chì era una versione estesa di l'ughjettu articulu, tippu di Articulu pienu. In generale, sta implementazione ùn porta nunda di terribili in sè stessu - hè assai simplice, si pò ancu dì ingenu, ma estremamente comprensibile. Se resettate u modulu ogni volta chì cambiate a strada, pudete ancu campà cun ellu. In ogni casu, si move trà l'articuli articuli, per esempiu /feed → /all, hè garantitu per scaccià tuttu ciò chì hè in relazione cù l'alimentazione persunale, postu chì avemu solu unu Lista di l'articuli, in quale avete bisognu di mette novi dati. Questu ci porta di novu à a duplicazione di e dumande.

Dopu avè cullatu tuttu ciò chì aghju pussutu scavà nantu à u tema, aghju formulatu una nova struttura statale è l'aghju presentata à i mo culleghi. E discussioni eranu longu, ma à a fine l'argumenti in favore anu più di i dubbiti, è aghju cuminciatu à implementà.

A logica di una suluzione hè megliu revelata in dui passi. Prima pruvemu di disaccoppià u modulu Vuex da e pagine è ligà direttamente à e rotte. Iè, ci sarà un pocu di più dati in a tenda, i getters diventeranu un pocu più cumplessu, ma ùn caricaremu micca articuli duie volte. Per a versione mobile, questu hè forse l'argumentu più forte. Serà qualcosa cusì:

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

Ma chì si listi di l'articuli ponu sovrappone trà parechje rotte è chì si vulemu reutilizà e dati di l'ughjettu articulu per rende a pagina di post, trasfurmendu in Articulu pienu? In questu casu, saria più logicu di utilizà una tale struttura:

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

Lista di l'articuli quì hè solu un tipu di repository di articuli. Tutti l'articuli chì sò stati scaricati durante a sessione d'utilizatore. Trattemu cù a massima cura, perchè questu hè u trafficu chì pò esse scaricatu da u dulore in qualchì locu in u metro trà stazioni, è certamente ùn vulemu micca causà stu dulore à l'utilizatore di novu forzendulu à carica dati chì hà digià. scaricatu. Un ughjettu ArticuliIds hè solu un array di ID (cum'è "ligami") à l'uggetti articulu. Sta struttura permette di evità di duplicà e dati cumuni à e rotte è di riutilizà l'ughjettu articulu quandu rende una pagina di post unendu dati estesi in questu.

L'output di a lista di l'articuli hè diventatu ancu più trasparente: u cumpunente iteratore iterate à traversu l'array cù l'ID di l'articuli è tira u cumpunente di teaser di l'articulu, passendu l'Id cum'è un prop, è u cumpunente di u zitellu, à u turnu, recupera i dati necessarii da Lista di l'articuli. Quandu andate à a pagina di publicazione, avemu da ottene a data digià esistente Lista di l'articuli, Facemu una dumanda per ottene e dati mancanti è solu aghjunghje à l'ughjettu esistenti.

Perchè questu approcciu hè megliu? Comu aghju scrittu sopra, stu approcciu hè più gentile cù u rispettu à i dati scaricati è vi permette di reutilizà. Ma in più di questu, apre a strada à parechje pussibulità novi chì si adattanu perfettamente à una tale architettura. Per esempiu, polling and loading articles in the feed as they appear. Pudemu simpricimenti mette l'ultimi posti in un "almacenamiento" Lista di l'articuli, salvà una lista separata di novi ID in ArticuliIds è avvisà l'utilizatore nantu à questu. Quandu clicchemu nantu à u buttone "Mostra novi publicazioni", simpricimenti inseriremu novi Ids in u principiu di l'array di a lista attuale di articuli è tuttu funziona quasi magicamente.

Facendu u scaricamentu più piacevule

U ghjacciu nantu à a torta di refactoring hè u cuncettu di scheletri, chì face u prucessu di scaricamentu di cuntenutu in un Internet lentu un pocu menu disgusting. Ùn ci era micca discussioni nantu à questa materia u percorsu da l'idea à u prototipu hà pigliatu literalmente duie ore. U disignu s'hè praticamente disegnatu, è avemu amparatu à i nostri cumpunenti à rende blocchi div simplici, appena tremuli mentre aspittendu dati. Subjettivamenti, stu approcciu di carica in realtà riduce a quantità di l'hormone di stress in u corpu di l'utilizatori. U scheletru s'assumiglia cusì:

Logs di sviluppatori front-end Habr: refactoring è riflessione
Habraloading

Riflessi

Sò sei mesi chì aghju travagliatu in Habré è i mo amichi si dumandanu sempre : bè, cumu ti piace quì ? Va bè, còmode - sì. Ma ci hè qualcosa chì face stu travagliu sfarente di l'altri. Aghju travagliatu in squadre chì eranu completamente indifferenti à u so pruduttu, ùn sapianu o capiscenu quale eranu i so utilizatori. Ma quì tuttu hè diversu. Quì vi sentite rispunsevuli di ciò chì fate. In u prucessu di sviluppà una funzione, diventate parzialmente u so pruprietariu, participà à tutte e riunioni di u produttu ligati à a vostra funziunalità, fate suggerimenti è pigliate decisioni. Fà un pruduttu chì aduprate ogni ghjornu stessu hè assai bellu, ma scrive u codice per e persone chì sò prubabilmente megliu da voi hè solu un sensu incredibile (senza sarcasmu).

Dopu a liberazione di tutti questi cambiamenti, avemu ricevutu feedback pusitivu, è era assai, assai bellu. Hè inspirante. Grazie! Scrivite più.

Permettemu di ricurdà chì dopu à e variàbili glubale avemu decisu di cambià l'architettura è attribuisce a capa proxy in una istanza separata. L'architettura di "dui nodi" hà digià righjuntu a liberazione in forma di teste beta publica. Avà qualcunu pò cambià à questu è aiutanu à fà megliu Habr mobile. Hè tuttu per oghje. Seraghju felice di risponde à tutte e vostre dumande in i cumenti.

Source: www.habr.com