Habr front-end developer logs: refactoring at reflecting

Habr front-end developer logs: refactoring at reflecting

Palagi akong interesado sa kung paano nakabalangkas ang Habr mula sa loob, kung paano nakaayos ang daloy ng trabaho, kung paano nakabalangkas ang mga komunikasyon, anong mga pamantayan ang ginagamit at kung paano karaniwang isinusulat ang code dito. Buti na lang at nakakuha ako ng ganitong pagkakataon, dahil kamakailan lang ay naging bahagi ako ng habra team. Gamit ang halimbawa ng isang maliit na refactoring ng mobile na bersyon, susubukan kong sagutin ang tanong: ano ang pakiramdam na magtrabaho dito sa harap. Sa programa: Node, Vue, Vuex at SSR na may sarsa mula sa mga tala tungkol sa personal na karanasan sa Habr.

Ang unang bagay na kailangan mong malaman tungkol sa development team ay kakaunti kami. Hindi sapat - ito ay tatlong harap, dalawang likod at ang teknikal na lead ng lahat ng Habr - Baxley. Siyempre, mayroon ding isang tester, isang taga-disenyo, tatlong Vadim, isang walis ng himala, isang espesyalista sa marketing at iba pang mga Bumburum. Ngunit mayroon lamang anim na direktang nag-aambag sa mga mapagkukunan ni Habr. Ito ay medyo bihira - isang proyekto na may multimillion-dollar na madla, na mula sa labas ay mukhang isang higanteng negosyo, sa katotohanan ay mas mukhang isang maaliwalas na startup na may pinakamababang istraktura ng organisasyon na posible.

Tulad ng maraming iba pang kumpanya ng IT, ipinapahayag ni Habr ang mga Agile na ideya, mga kasanayan sa CI, at iyon lang. Ngunit ayon sa aking damdamin, ang Habr bilang isang produkto ay higit na umuunlad sa mga alon kaysa patuloy. Kaya, para sa ilang magkakasunod na sprint, masigasig kaming nagko-code ng isang bagay, nagdidisenyo at muling nagdidisenyo, nasira ang isang bagay at nag-aayos nito, nagresolba ng mga tiket at lumikha ng mga bago, tumuntong sa isang kalaykay at nag-shoot sa aming mga paa, upang tuluyang mailabas ang tampok sa produksyon. At pagkatapos ay darating ang isang tiyak na tahimik, isang panahon ng muling pagpapaunlad, oras upang gawin kung ano ang nasa "importante-not urgent" quadrant.

Ito ay tiyak na ito "off-season" sprint na tatalakayin sa ibaba. Sa pagkakataong ito, isinama nito ang refactoring ng mobile na bersyon ng Habr. Sa pangkalahatan, ang kumpanya ay may mataas na pag-asa para dito, at sa hinaharap dapat nitong palitan ang buong zoo ng mga pagkakatawang-tao ni Habr at maging isang unibersal na cross-platform na solusyon. Balang araw magkakaroon ng adaptive na layout, PWA, offline mode, pag-customize ng user, at marami pang ibang kawili-wiling bagay.

Itakda natin ang gawain

Minsan, sa isang ordinaryong stand-up, ang isa sa harap ay nagsalita tungkol sa mga problema sa arkitektura ng bahagi ng mga komento ng mobile na bersyon. Dahil dito, nag-organisa kami ng micro-meeting sa format ng group psychotherapy. Ang bawat isa ay nagsalit-salit sa pagsasabi kung saan masakit, lahat ng bagay ay itinala nila sa papel, sila ay nakiramay, sila ay naunawaan, maliban sa walang pumalakpak. Ang resulta ay isang listahan ng 20 mga problema, na ginawang malinaw na ang mobile Habr ay mayroon pa ring mahaba at matinik na landas tungo sa tagumpay.

Pangunahing nag-aalala ako tungkol sa kahusayan ng paggamit ng mapagkukunan at kung ano ang tinatawag na isang makinis na interface. Araw-araw, sa home-work-home route, nakita ko ang aking lumang telepono na desperadong sinusubukang magpakita ng 20 headline sa feed. Ito ay mukhang ganito:

Habr front-end developer logs: refactoring at reflectingMobile Habr interface bago refactoring

Anong nangyayari dito? Sa madaling salita, inihatid ng server ang HTML page sa lahat sa parehong paraan, hindi alintana kung naka-log in ang user o hindi. Pagkatapos ay ni-load ang client JS at humiling muli ng kinakailangang data, ngunit inaayos para sa awtorisasyon. Ibig sabihin, dalawang beses talaga naming ginawa ang parehong trabaho. Ang interface ay kumikislap, at ang user ay nag-download ng isang mahusay na daang dagdag na kilobytes. Sa detalye ang lahat ay mukhang mas katakut-takot.

Habr front-end developer logs: refactoring at reflectingLumang SSR-CSR scheme. Ang pahintulot ay posible lamang sa mga yugto ng C3 at C4, kapag ang Node JS ay hindi abala sa pagbuo ng HTML at maaaring mag-proxy ng mga kahilingan sa API.

Ang aming arkitektura noong panahong iyon ay napakatumpak na inilarawan ng isa sa mga gumagamit ng Habr:

Ang mobile na bersyon ay crap. Sinasabi ko ito ng ganito. Isang kakila-kilabot na kumbinasyon ng SSR at CSR.

Kailangan naming aminin ito, gaano man ito kalungkot.

Sinuri ko ang mga opsyon, gumawa ng ticket sa Jira na may paglalarawan sa antas na "masama ito ngayon, gawin mo ito ng tama" at nabulok ang gawain sa malawak na mga hakbang:

  • muling gamitin ang data,
  • bawasan ang bilang ng mga muling pagguhit,
  • alisin ang mga duplicate na kahilingan,
  • gawing mas malinaw ang proseso ng paglo-load.

Muli nating gamitin ang data

Sa teorya, ang pag-render sa panig ng server ay idinisenyo upang malutas ang dalawang problema: hindi magdusa mula sa mga limitasyon ng search engine sa mga tuntunin ng SPA indexing at pagbutihin ang sukatan FMP (hindi maiwasang lumala TTI). Sa isang klasikong senaryo na sa wakas binuo sa Airbnb noong 2013 taon (nasa Backbone.js pa rin), ang SSR ay ang parehong isomorphic JS application na tumatakbo sa kapaligiran ng Node. Ipinapadala lang ng server ang nabuong layout bilang tugon sa kahilingan. Pagkatapos ay nangyayari ang rehydration sa panig ng kliyente, at pagkatapos ay gumagana ang lahat nang walang pag-reload ng pahina. Para kay Habr, tulad ng para sa maraming iba pang mga mapagkukunan na may nilalamang teksto, ang pag-render ng server ay isang kritikal na elemento sa pagbuo ng mapagkaibigang relasyon sa mga search engine.

Sa kabila ng katotohanan na higit sa anim na taon na ang lumipas mula noong pagdating ng teknolohiya, at sa panahong ito maraming tubig ang talagang lumipad sa ilalim ng tulay sa front-end na mundo, para sa maraming mga developer ang ideyang ito ay natatakpan pa rin ng lihim. Hindi kami tumabi at naglunsad ng Vue application na may suporta sa SSR sa produksyon, kulang ng isang maliit na detalye: hindi namin ipinadala ang paunang estado sa kliyente.

Bakit? Walang eksaktong sagot sa tanong na ito. Alinman sa hindi nila nais na dagdagan ang laki ng tugon mula sa server, o dahil sa isang bungkos ng iba pang mga problema sa arkitektura, o hindi ito nag-alis. Sa isang paraan o iba pa, ang pagtatapon ng estado at muling paggamit ng lahat ng ginawa ng server ay tila angkop at kapaki-pakinabang. Ang gawain ay talagang walang halaga - estado ay iniksyon lamang sa konteksto ng pagpapatupad, at awtomatikong idinaragdag ito ng Vue sa nabuong layout bilang isang pandaigdigang variable: window.__INITIAL_STATE__.

Ang isa sa mga problema na lumitaw ay ang kawalan ng kakayahan na i-convert ang mga paikot na istruktura sa JSON (paikot na sanggunian); ay nalutas sa pamamagitan lamang ng pagpapalit ng mga naturang istruktura ng kanilang mga flat counterparts.

Bilang karagdagan, kapag nakikitungo sa nilalaman ng UGC, dapat mong tandaan na ang data ay dapat na ma-convert sa mga entity ng HTML upang hindi masira ang HTML. Para sa mga layuning ito ginagamit namin he.

Pag-minimize ng mga redraw

Gaya ng nakikita mo mula sa diagram sa itaas, sa aming kaso, ang isang Node JS instance ay gumaganap ng dalawang function: SSR at "proxy" sa API, kung saan nangyayari ang pahintulot ng user. Dahil sa sitwasyong ito, imposibleng pahintulutan habang tumatakbo ang JS code sa server, dahil single-threaded ang node, at kasabay ang function ng SSR. Iyon ay, ang server ay hindi maaaring magpadala ng mga kahilingan sa sarili nito habang ang callstack ay abala sa isang bagay. Ito ay na-update namin ang estado, ngunit ang interface ay hindi tumigil sa pag-twitch, dahil ang data sa kliyente ay kailangang i-update na isinasaalang-alang ang session ng gumagamit. Kailangan naming turuan ang aming application na ilagay ang tamang data sa paunang estado, na isinasaalang-alang ang pag-login ng user.

Mayroon lamang dalawang solusyon sa problema:

  • ilakip ang data ng pahintulot sa mga kahilingan sa cross-server;
  • hatiin ang mga layer ng Node JS sa dalawang magkahiwalay na pagkakataon.

Ang unang solusyon ay nangangailangan ng paggamit ng mga global variable sa server, at ang pangalawa ay pinalawig ang deadline para sa pagkumpleto ng gawain nang hindi bababa sa isang buwan.

Paano gumawa ng isang pagpipilian? Si Habr ay madalas na gumagalaw sa landas ng hindi bababa sa pagtutol. Sa di-pormal, mayroong pangkalahatang pagnanais na bawasan ang cycle mula sa ideya patungo sa prototype sa pinakamababa. Ang modelo ng saloobin sa produkto ay medyo nakapagpapaalaala sa mga postulate ng booking.com, na ang pagkakaiba lang ay mas sineseryoso ni Habr ang feedback ng user at nagtitiwala sa iyo, bilang isang developer, na gumawa ng mga ganoong desisyon.

Kasunod ng lohika na ito at ang aking sariling pagnanais na mabilis na malutas ang problema, pinili ko ang mga pandaigdigang variable. At, tulad ng madalas na nangyayari, kailangan mong bayaran ang mga ito sa lalong madaling panahon o huli. Nagbayad kami halos kaagad: nagtrabaho kami sa katapusan ng linggo, na-clear ang mga kahihinatnan, nagsulat postmortem at nagsimulang hatiin ang server sa dalawang bahagi. Ang error ay napaka-hangal, at ang bug na kinasasangkutan nito ay hindi madaling magparami. At oo, ito ay isang kahihiyan para dito, ngunit sa isang paraan o iba pa, natitisod at umuungol, ang aking PoC na may mga pandaigdigang variable ay napunta sa produksyon at matagumpay na gumagana habang naghihintay para sa paglipat sa isang bagong "two-node" na arkitektura. Ito ay isang mahalagang hakbang, dahil pormal na nakamit ang layunin - natutunan ng SSR na maghatid ng ganap na handa nang gamitin na pahina, at ang UI ay naging mas kalmado.

Habr front-end developer logs: refactoring at reflectingMobile Habr interface pagkatapos ng unang yugto ng refactoring

Sa huli, ang arkitektura ng SSR-CSR ng mobile na bersyon ay humahantong sa larawang ito:

οΏΌHabr front-end developer logs: refactoring at reflecting"Two-node" SSR-CSR circuit. Ang Node JS API ay palaging handa para sa asynchronous na I/O at hindi na-block ng SSR function, dahil ang huli ay matatagpuan sa isang hiwalay na pagkakataon. Hindi kailangan ang query chain #3.

Pag-aalis ng mga duplicate na kahilingan

Matapos maisagawa ang mga manipulasyon, ang unang pag-render ng pahina ay hindi na nagdulot ng epilepsy. Ngunit ang karagdagang paggamit ng Habr sa SPA mode ay nagdulot pa rin ng kalituhan.

Dahil ang batayan ng daloy ng user ay mga transition ng form listahan ng mga artikulo β†’ artikulo β†’ komento at vice versa, mahalagang i-optimize ang pagkonsumo ng mapagkukunan ng chain na ito sa unang lugar.

Habr front-end developer logs: refactoring at reflectingAng pagbabalik sa post feed ay nagdudulot ng bagong kahilingan sa data

Hindi na kailangang maghukay ng malalim. Sa screencast sa itaas, makikita mo na muling hinihiling ng application ang listahan ng mga artikulo kapag nag-swipe pabalik, at sa panahon ng kahilingan ay hindi namin nakikita ang mga artikulo, na nangangahulugang nawawala ang nakaraang data sa isang lugar. Mukhang ang bahagi ng listahan ng artikulo ay gumagamit ng isang lokal na estado at nawala ito sa pagsira. Sa katunayan, ang application ay gumamit ng isang pandaigdigang estado, ngunit ang arkitektura ng Vuex ay binuo nang direkta: ang mga module ay nakatali sa mga pahina, na kung saan ay nakatali sa mga ruta. Bukod dito, ang lahat ng mga module ay "disposable" - bawat kasunod na pagbisita sa pahina ay muling isinulat ang buong module:

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

Sa kabuuan, mayroon kaming isang module Listahan ng mga Artikulo, na naglalaman ng mga bagay na may uri Artikulo at modyul Pahina Artikulo, na isang pinahabang bersyon ng object Artikulo, medyo Puno ng Artikulo. Sa pangkalahatan, ang pagpapatupad na ito ay hindi nagdadala ng anumang kahila-hilakbot sa sarili nito - ito ay napaka-simple, maaaring sabihin ng isang tao na walang muwang, ngunit lubos na nauunawaan. Kung ire-reset mo ang module sa tuwing babaguhin mo ang ruta, maaari mo ring mabuhay kasama nito. Gayunpaman, ang paglipat sa pagitan ng mga feed ng artikulo, halimbawa /pakain β†’ /lahat, ay garantisadong itapon ang lahat ng nauugnay sa personal na feed, dahil isa lang ang mayroon kami Listahan ng mga Artikulo, kung saan kailangan mong maglagay ng bagong data. Ito ay muling humahantong sa amin sa pagdoble ng mga kahilingan.

Nakolekta ang lahat ng bagay na nakuha ko sa paksa, bumalangkas ako ng isang bagong istraktura ng estado at ipinakita ito sa aking mga kasamahan. Ang mga talakayan ay mahaba, ngunit sa huli ang mga argumento na pabor ay higit sa mga pagdududa, at sinimulan ko ang pagpapatupad.

Ang lohika ng isang solusyon ay pinakamahusay na inihayag sa dalawang hakbang. Una, sinusubukan naming i-decouple ang Vuex module mula sa mga pahina at direktang magbigkis sa mga ruta. Oo, magkakaroon ng kaunting data sa tindahan, ang mga getter ay magiging mas kumplikado, ngunit hindi kami maglo-load ng mga artikulo nang dalawang beses. Para sa mobile na bersyon, ito marahil ang pinakamatibay na argumento. Magiging ganito ang hitsura nito:

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

Ngunit paano kung ang mga listahan ng artikulo ay maaaring mag-overlap sa pagitan ng maraming ruta at paano kung gusto naming muling gamitin ang object data Artikulo upang i-render ang post page, gawing ito Puno ng Artikulo? Sa kasong ito, magiging mas lohikal na gumamit ng gayong istraktura:

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

Listahan ng mga Artikulo narito ito ay isang uri lamang ng imbakan ng mga artikulo. Lahat ng mga artikulo na na-download sa session ng user. Tinatrato namin sila nang may lubos na pag-iingat, dahil ito ay trapiko na maaaring na-download sa pamamagitan ng sakit sa isang lugar sa metro sa pagitan ng mga istasyon, at tiyak na hindi namin nais na magdulot muli ng sakit na ito sa gumagamit sa pamamagitan ng pagpilit sa kanya na mag-load ng data na mayroon na siya. na-download. Isang bagay Mga ArtikuloId ay simpleng hanay ng mga ID (na parang "mga link") sa mga bagay Artikulo. Binibigyang-daan ka ng istrukturang ito na maiwasan ang pagdoble ng data na karaniwan sa mga ruta at muling paggamit ng bagay Artikulo kapag nag-render ng isang post page sa pamamagitan ng pagsasama ng pinalawak na data dito.

Ang output ng listahan ng mga artikulo ay naging mas transparent din: ang iterator component ay umuulit sa array na may mga article ID at kumukuha ng article teaser component, na ipinapasa ang Id bilang prop, at ang child component, naman, ay kinukuha ang kinakailangang data mula sa Listahan ng mga Artikulo. Kapag pumunta ka sa pahina ng publikasyon, nakukuha namin ang dati nang petsa mula sa Listahan ng mga Artikulo, gumawa kami ng kahilingan upang makuha ang nawawalang data at idagdag lamang ito sa umiiral na bagay.

Bakit mas mahusay ang diskarteng ito? Tulad ng isinulat ko sa itaas, ang diskarte na ito ay mas banayad na may paggalang sa na-download na data at nagbibigay-daan sa iyong muling gamitin ito. Ngunit bukod dito, nagbubukas ito ng daan patungo sa ilang mga bagong posibilidad na akmang-akma sa gayong arkitektura. Halimbawa, ang pagboto at pag-load ng mga artikulo sa feed habang lumilitaw ang mga ito. Maaari lang naming ilagay ang pinakabagong mga post sa isang "imbakan" Listahan ng mga Artikulo, mag-save ng hiwalay na listahan ng mga bagong ID sa Mga ArtikuloId at abisuhan ang gumagamit tungkol dito. Kapag nag-click kami sa pindutang "Ipakita ang mga bagong publikasyon", maglalagay lang kami ng mga bagong Id sa simula ng hanay ng kasalukuyang listahan ng mga artikulo at lahat ay gagana nang halos mahiwagang.

Ginagawang mas kasiya-siya ang pag-download

Ang icing sa refactoring cake ay ang konsepto ng mga skeleton, na ginagawang hindi gaanong kasuklam-suklam ang proseso ng pag-download ng content sa mabagal na Internet. Walang mga talakayan sa bagay na ito; ang landas mula sa ideya hanggang sa prototype ay literal na tumagal ng dalawang oras. Ang disenyo ay halos gumuhit mismo, at tinuruan namin ang aming mga bahagi na mag-render ng simple, halos hindi kumukutitap na mga bloke ng div habang naghihintay ng data. Sa pangkalahatan, ang diskarteng ito sa pag-load ay talagang binabawasan ang dami ng mga stress hormone sa katawan ng gumagamit. Ang balangkas ay ganito ang hitsura:

Habr front-end developer logs: refactoring at reflecting
Habraloading

Nagmumuni-muni

Anim na buwan na akong nagtatrabaho sa HabrΓ© at nagtatanong pa rin ang mga kaibigan ko: well, how do you like it there? Okay, komportable - oo. Ngunit may isang bagay na nagpapaiba sa gawaing ito sa iba. Nagtrabaho ako sa mga koponan na ganap na walang malasakit sa kanilang produkto, hindi alam o naiintindihan kung sino ang kanilang mga gumagamit. Ngunit dito lahat ay naiiba. Dito nararamdaman mong responsable ka sa iyong ginagawa. Sa proseso ng pagbuo ng isang feature, ikaw ay bahagyang nagiging may-ari nito, nakikibahagi sa lahat ng mga pulong ng produkto na nauugnay sa iyong functionality, gumawa ng mga mungkahi at ikaw mismo ang gumagawa ng mga desisyon. Ang paggawa ng isang produkto na ginagamit mo araw-araw ay napaka-cool, ngunit ang pagsulat ng code para sa mga taong malamang na mas mahusay dito kaysa sa iyo ay isang hindi kapani-paniwalang pakiramdam (walang panunuya).

Matapos ilabas ang lahat ng mga pagbabagong ito, nakatanggap kami ng positibong feedback, at ito ay napaka, napakaganda. Nakaka-inspire. Salamat! Sumulat pa.

Ipaalala ko sa iyo na pagkatapos ng mga pandaigdigang variable ay nagpasya kaming baguhin ang arkitektura at ilaan ang proxy layer sa isang hiwalay na pagkakataon. Ang arkitektura ng "two-node" ay umabot na sa paglabas sa anyo ng pampublikong pagsubok sa beta. Ngayon kahit sino ay maaaring lumipat dito at tulungan kaming gawing mas mahusay ang mobile Habr. Yan lamang para sa araw na ito. Ikalulugod kong sagutin ang lahat ng iyong mga katanungan sa mga komento.

Pinagmulan: www.habr.com

Magdagdag ng komento