Habrin käyttöliittymän kehittäjälokit: uudelleenmuodostus ja heijastus

Habrin käyttöliittymän kehittäjälokit: uudelleenmuodostus ja heijastus

Olen aina ollut kiinnostunut siitä, miten Habr on rakennettu sisältä käsin, miten työnkulku rakentuu, miten viestintä rakentuu, mitä standardeja käytetään ja miten koodi yleensä kirjoitetaan tähän. Onneksi sain tällaisen mahdollisuuden, koska liityin hiljattain habra-tiimiin. Mobiiliversion pienen refaktoroinnin esimerkin avulla yritän vastata kysymykseen: millaista on työskennellä täällä edessä. Ohjelmassa: Node, Vue, Vuex ja SSR kastikkeella muistiinpanoista henkilökohtaisesta kokemuksesta Habr.

Ensimmäinen asia, joka sinun on tiedettävä kehitystiimistä, on, että meitä on vähän. Ei riitä - nämä ovat kolme etuosaa, kaksi takapuolta ja kaikkien Habr - Baxleyn tekninen johto. Mukana on tietysti myös testaaja, suunnittelija, kolme Vadimia, ihmeluuta, markkinointiasiantuntija ja muita Bumburumia. Mutta Habrin lähteillä on vain kuusi suoraa avustajaa. Tämä on melko harvinaista - monimiljoonaisen yleisön projekti, joka näyttää ulkopuolelta jättiläisyritykseltä, todellisuudessa näyttää enemmän kodikkaalta startupilta, jolla on mahdollisimman tasainen organisaatiorakenne.

Kuten monet muut IT-yritykset, Habr tunnustaa ketterät ideat, CI-käytännöt ja siinä kaikki. Mutta tunteideni mukaan Habr tuotteena kehittyy enemmän aaltoina kuin jatkuvasti. Joten usean sprintin peräkkäin koodaamme ahkerasti jotain, suunnittelemme ja suunnittelemme uudelleen, rikomme jotain ja korjaamme, ratkaisemme liput ja luomme uusia, astumme haravalle ja ammumme itseämme jalkaan, jotta ominaisuus vihdoin vapautuisi tuotantoon. Ja sitten tulee tietty tyynnytys, uudelleenkehityksen kausi, aika tehdä se, mikä on "tärkeän-ei kiireellisen" -kvadrantissa.

Juuri tästä "sesongin ulkopuolisesta" sprintistä keskustellaan alla. Tällä kertaa se sisälsi Habrin mobiiliversion uudelleenmuodostuksen. Yleisesti ottaen yrityksellä on suuret toiveet sen suhteen, ja tulevaisuudessa sen pitäisi korvata koko Habrin inkarnaatioiden eläintarha ja siitä tulee universaali monikäyttöinen ratkaisu. Jonakin päivänä tulee olemaan mukautuva asettelu, PWA, offline-tila, käyttäjän mukauttaminen ja monia muita mielenkiintoisia asioita.

Laitetaan tehtävä

Kerran tavallisessa stand-upissa yksi eturintamassa puhui mobiiliversion kommenttikomponentin arkkitehtuurin ongelmista. Tätä silmällä pitäen järjestimme mikrokokouksen ryhmäpsykoterapian muodossa. Kaikki kertoivat vuorotellen missä sattuu, he kirjasivat kaiken paperille, he tunsivat myötätuntoa, he ymmärsivät, paitsi että kukaan ei taputtanut. Tuloksena oli luettelo 20 ongelmasta, jotka tekivät selväksi, että mobiili Habrilla oli vielä pitkä ja hankala tie menestykseen.

Olin ensisijaisesti huolissani resurssitehokkuudesta ja niin sanotusta sujuvasta käyttöliittymästä. Joka päivä koti-työ-koti-reitillä näin vanhan puhelimeni yrittävän epätoivoisesti näyttää syötteessä 20 otsikkoa. Se näytti jotakuinkin tältä:

Habrin käyttöliittymän kehittäjälokit: uudelleenmuodostus ja heijastusMobile Habr -käyttöliittymä ennen uudelleenkäsittelyä

Mitä täällä tapahtuu? Lyhyesti sanottuna palvelin palveli HTML-sivua kaikille samalla tavalla riippumatta siitä, oliko käyttäjä kirjautunut sisään vai ei. Sitten asiakas JS ladataan ja pyytää tarvittavat tiedot uudelleen, mutta tarkistetaan valtuutusta varten. Eli teimme saman työn kahdesti. Käyttöliittymä välkkyi ja käyttäjä latasi reilu sata kilotavua ylimääräistä. Yksityiskohtaisesti kaikki näytti vielä pelottavammalta.

Habrin käyttöliittymän kehittäjälokit: uudelleenmuodostus ja heijastusVanha SSR-CSR-malli. Valtuutus on mahdollista vain vaiheissa C3 ja C4, kun Node JS ei ole varattu HTML:n luomiseen ja voi lähettää välityspyyntöjä API:lle.

Yksi Habr-käyttäjistä kuvaili tuon ajan arkkitehtuuriamme erittäin tarkasti:

Mobiiliversio on paska. Kerron sen niin kuin se on. Kamala SSR:n ja CSR:n yhdistelmä.

Meidän oli myönnettävä se, vaikka se oli kuinka surullista.

Arvioin vaihtoehdot, loin Jiraan lipun, jonka kuvaus oli "nyt on huono, tee se oikein" ja hahmottelin tehtävän laajasti:

  • käyttää tietoja uudelleen,
  • minimoida uudelleenpiirtämisen määrän,
  • poistaa päällekkäiset pyynnöt,
  • tehdä latausprosessista selkeämpi.

Käytetään dataa uudelleen

Teoriassa palvelinpuolen renderöinti on suunniteltu ratkaisemaan kaksi ongelmaa: se ei kärsisi hakukoneiden rajoituksista. SPA-indeksointi ja parantaa mittaria FMP (väistämättä pahenee TTI). Klassisessa skenaariossa, että vihdoin muotoiltu Airbnb:ssä vuonna 2013 vuoden (vielä Backbone.js:ssa), SSR on sama isomorfinen JS-sovellus, joka toimii Node-ympäristössä. Palvelin yksinkertaisesti lähettää luodun asettelun vastauksena pyyntöön. Sitten nesteytys tapahtuu asiakaspuolella, ja sitten kaikki toimii ilman sivujen uudelleenlatauksia. Habrille, kuten monille muille tekstisisältöä sisältäville resursseille, palvelimen renderöinti on kriittinen elementti ystävällisten suhteiden luomisessa hakukoneiden kanssa.

Huolimatta siitä, että tekniikan tulosta on kulunut yli kuusi vuotta ja tänä aikana sillan alla on todella paljon vettä lentänyt front-end-maailmassa, monille kehittäjille tämä ajatus on edelleen salaisuuden peitossa. Emme jääneet sivuun ja otimme Vue-sovelluksen SSR-tuella tuotantoon, josta puuttui yksi pieni yksityiskohta: emme lähettäneet alkuperäistä tilaa asiakkaalle.

Miksi? Tähän kysymykseen ei ole tarkkaa vastausta. Joko he eivät halunneet suurentaa palvelimelta tulevan vastauksen kokoa tai useiden muiden arkkitehtonisten ongelmien vuoksi, tai se ei yksinkertaisesti lähtenyt liikkeelle. Tavalla tai toisella tilan heittäminen ja kaiken palvelimen tekemän uudelleenkäyttö vaikuttaa varsin sopivalta ja hyödylliseltä. Tehtävä on itse asiassa triviaali - tila on yksinkertaisesti ruiskutettu suorituskontekstiin, ja Vue lisää sen automaattisesti luotuun asetteluun globaalina muuttujana: window.__INITIAL_STATE__.

Yksi esiin tulleista ongelmista on kyvyttömyys muuntaa syklisiä rakenteita JSON-muotoon (pyöreä viite); Ratkaistiin yksinkertaisesti korvaamalla tällaiset rakenteet niiden litteillä vastineilla.

Lisäksi, kun käsittelet UGC-sisältöä, sinun tulee muistaa, että tiedot tulee muuntaa HTML-kokonaisuuksiksi, jotta HTML ei riko. Näihin tarkoituksiin käytämme he.

Minimoi uudelleenpiirrokset

Kuten yllä olevasta kaaviosta näet, meidän tapauksessamme yksi Node JS -esiintymä suorittaa kaksi toimintoa: SSR ja "välityspalvelin" API:ssa, jossa käyttäjän valtuutus tapahtuu. Tämä seikka tekee valtuutuksen mahdottomaksi, kun JS-koodi on käynnissä palvelimella, koska solmu on yksisäikeinen ja SSR-toiminto on synkroninen. Toisin sanoen palvelin ei yksinkertaisesti voi lähettää pyyntöjä itselleen, kun puhelupino on varattu jollakin. Kävi ilmi, että päivitimme tilan, mutta käyttöliittymä ei lopettanut nykimistä, koska asiakkaan tiedot piti päivittää ottaen huomioon käyttäjän istunto. Meidän piti opettaa sovelluksemme asettamaan oikeat tiedot alkutilaan ottaen huomioon käyttäjän kirjautuminen.

Ongelmaan oli vain kaksi ratkaisua:

  • liittää valtuutustiedot palvelinten välisiin pyyntöihin;
  • jakaa Node JS -tasot kahteen erilliseen esiintymään.

Ensimmäinen ratkaisu edellytti globaalien muuttujien käyttöä palvelimella ja toinen pidensi tehtävän suorittamisen määräaikaa vähintään kuukaudella.

Kuinka tehdä valinta? Habr liikkuu usein vähimmän vastuksen polkua pitkin. Epävirallisesti on olemassa yleinen halu vähentää sykli ideasta prototyyppiin minimiin. Asennemalli tuotteeseen muistuttaa jossain määrin booking.com-sivuston postulaatteja, sillä ainoa ero on, että Habr ottaa käyttäjäpalautteen paljon vakavammin ja luottaa sinuun kehittäjänä tällaisten päätösten tekemisessä.

Noudattaen tätä logiikkaa ja omaa haluani ratkaista ongelma nopeasti, valitsin globaalit muuttujat. Ja kuten usein tapahtuu, sinun on maksettava niistä ennemmin tai myöhemmin. Maksoimme melkein heti: teimme töitä viikonloppuna, selvitimme seuraukset, kirjoitti jälkipuinti ja alkoi jakaa palvelimen kahteen osaan. Virhe oli erittäin typerä, eikä siihen liittyvää vikaa ollut helppo toistaa. Ja kyllä, tämä on sääli, mutta tavalla tai toisella, kompastuen ja voihkien, globaaleilla muuttujilla varustettu PoCni meni kuitenkin tuotantoon ja toimii melko menestyksekkäästi odottaessaan siirtymistä uuteen "kaksisolmun" arkkitehtuuriin. Tämä oli tärkeä askel, sillä muodollisesti tavoite saavutettiin - SSR oppi toimittamaan täysin käyttövalmiin sivun ja käyttöliittymä muuttui paljon rauhallisemmaksi.

Habrin käyttöliittymän kehittäjälokit: uudelleenmuodostus ja heijastusMobile Habr -käyttöliittymä ensimmäisen refaktorointivaiheen jälkeen

Viime kädessä mobiiliversion SSR-CSR-arkkitehtuuri johtaa tähän kuvaan:

Habrin käyttöliittymän kehittäjälokit: uudelleenmuodostus ja heijastus"Kahden solmun" SSR-CSR-piiri. Node JS API on aina valmis asynkroniselle I/O:lle, eikä SSR-toiminto estä sitä, koska jälkimmäinen sijaitsee erillisessä ilmentymässä. Kyselyketjua #3 ei tarvita.

Päällekkäisten pyyntöjen poistaminen

Käsittelyjen jälkeen sivun alkuperäinen renderöinti ei enää aiheuttanut epilepsiaa. Mutta Habrin jatkokäyttö SPA-tilassa aiheutti silti hämmennystä.

Koska käyttäjävirran perusta on muodon siirtymät artikkeliluettelo → artikkeli → kommentit ja päinvastoin, oli tärkeää optimoida tämän ketjun resurssien kulutus ensisijaisesti.

Habrin käyttöliittymän kehittäjälokit: uudelleenmuodostus ja heijastusPalaaminen viestisyötteeseen provosoi uuden tietopyynnön

Ei tarvinnut kaivaa syvälle. Yllä olevasta kuvalähetyksestä näet, että sovellus pyytää uudelleen artikkeliluetteloa taaksepäin pyyhkäisemällä, ja pyynnön aikana emme näe artikkeleita, mikä tarkoittaa, että aiemmat tiedot katoavat jonnekin. Näyttää siltä, ​​että artikkeliluettelokomponentti käyttää paikallista tilaa ja menettää sen tuhottaessa. Itse asiassa sovellus käytti globaalia tilaa, mutta Vuex-arkkitehtuuri rakennettiin suoraan: moduulit on sidottu sivuihin, jotka puolestaan ​​​​on sidottu reitteihin. Lisäksi kaikki moduulit ovat "kertakäyttöisiä" - jokainen seuraava sivukäynti kirjoitti koko moduulin uudelleen:

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

Kaiken kaikkiaan meillä oli moduuli Artikkelilista, joka sisältää tyyppisiä objekteja Artikkeli ja moduuli Sivuartikkeli, joka oli objektin laajennettu versio Artikkeli, eräänlainen Artikkeli täynnä. Yleisesti ottaen tämä toteutus ei sisällä mitään kauheaa sinänsä - se on hyvin yksinkertainen, voisi jopa sanoa, että naiivi, mutta erittäin ymmärrettävää. Jos nollaat moduulin joka kerta, kun muutat reittiä, voit jopa elää sen kanssa. Kuitenkin esimerkiksi artikkelisyötteiden välillä liikkuminen /feed → /all, heittää taatusti roskiin kaiken henkilökohtaiseen syötteeseen liittyvän, koska meillä on vain yksi Artikkelilista, johon sinun on lisättävä uusia tietoja. Tämä johtaa jälleen päällekkäisiin pyyntöihin.

Kerättyään kaiken, mitä pystyin kaivamaan esiin aiheesta, muotoilin uuden valtiorakenteen ja esittelin sen kollegoilleni. Keskustelut olivat pitkiä, mutta lopulta puoltavat argumentit ylittivät epäilykset, ja aloitin toteuttamisen.

Ratkaisun logiikka paljastuu parhaiten kahdessa vaiheessa. Ensin yritämme irrottaa Vuex-moduulin sivuista ja sitoutua suoraan reitteihin. Kyllä, myymälässä tulee olemaan hieman enemmän dataa, getterit muuttuvat hieman monimutkaisemmiksi, mutta emme lataa artikkeleita kahdesti. Mobiiliversiolle tämä on ehkä vahvin argumentti. Se näyttää suunnilleen tältä:

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

Mutta entä jos artikkeliluettelot voivat mennä päällekkäin useiden reittien välillä ja entä jos haluamme käyttää objektitietoja uudelleen Artikkeli hahmontamaan viestisivun ja muuttamaan sen muotoon Artikkeli täynnä? Tässä tapauksessa olisi loogisempaa käyttää tällaista rakennetta:

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

Artikkelilista tässä se on vain eräänlainen artikkeleiden arkisto. Kaikki käyttäjäistunnon aikana ladatut artikkelit. Suhtaudumme niihin äärimmäisellä varovaisuudella, koska tämä on liikennettä, joka on saatettu ladata kivun kautta jossain metrossa asemien välillä, emmekä todellakaan halua aiheuttaa tätä tuskaa käyttäjälle uudelleen pakottamalla hänet lataamaan tietoja, jotka hän on jo hankkinut. ladattu. Esine Artikkelin tunnukset on yksinkertaisesti joukko tunnisteita (ikään kuin "linkkejä") objekteihin Artikkeli. Tämän rakenteen avulla voit välttää reiteille yhteisten tietojen päällekkäisyyden ja objektin uudelleenkäytön Artikkeli hahmonnettaessa viestisivua yhdistämällä siihen laajennettuja tietoja.

Myös artikkeliluettelon tuloste on muuttunut läpinäkyvämmäksi: iteraattorikomponentti iteroi artikkelitunnuksilla olevan taulukon läpi ja piirtää artikkelin teaser-komponentin välittäen tunnuksen rekvisiittana, ja alikomponentti puolestaan ​​hakee tarvittavat tiedot Artikkelilista. Kun siirryt julkaisusivulle, saamme jo olemassa olevan päivämäärän alkaen Artikkelilista, teemme pyynnön hankkia puuttuvat tiedot ja yksinkertaisesti lisäämme ne olemassa olevaan objektiin.

Miksi tämä lähestymistapa on parempi? Kuten edellä kirjoitin, tämä lähestymistapa on hellävaraisempi ladattujen tietojen suhteen ja antaa sinun käyttää niitä uudelleen. Mutta tämän lisäksi se avaa tien uusille mahdollisuuksille, jotka sopivat täydellisesti tällaiseen arkkitehtuuriin. Esimerkiksi kyselyjen tekeminen ja artikkelien lataaminen syötteeseen niiden ilmestyessä. Voimme yksinkertaisesti laittaa uusimmat viestit "varastoon" Artikkelilista, tallenna erillinen luettelo uusista tunnuksista Artikkelin tunnukset ja ilmoittaa siitä käyttäjälle. Kun napsautamme "Näytä uudet julkaisut" -painiketta, lisäämme vain uudet tunnukset nykyisen artikkeliluettelon joukon alkuun ja kaikki toimii melkein taianomaisesti.

Tekee lataamisesta nautinnollisempaa

Kirsikkana refaktorointikakun päällä on luurankojen käsite, joka tekee sisällön lataamisesta hitaasta Internetistä hieman vähemmän inhottavaa. Asiasta ei keskusteltu, polku ideasta prototyyppiin kesti kirjaimellisesti kaksi tuntia. Suunnittelu käytännössä piirsi itsensä, ja opetimme komponenttimme renderöimään yksinkertaisia, tuskin välkkyviä div-lohkoja odottaessamme tietoja. Subjektiivisesti tämä lähestymistapa lataamiseen itse asiassa vähentää stressihormonien määrää käyttäjän kehossa. Luuranko näyttää tältä:

Habrin käyttöliittymän kehittäjälokit: uudelleenmuodostus ja heijastus
Habraloading

Heijastava

Olen työskennellyt Habressa kuusi kuukautta ja ystäväni kysyvät edelleen: no, mitä pidät siellä? Okei, mukava - kyllä. Mutta jokin tekee tästä työstä erilaisen kuin muut. Työskentelin tiimeissä, jotka suhtautuivat täysin välinpitämättömästi tuotteisiinsa, eivätkä tienneet tai ymmärtäneet käyttäjiään. Mutta täällä kaikki on toisin. Täällä tunnet vastuun tekemisistäsi. Ominaisuutta kehitettäessä tulet osittain sen omistajaksi, osallistut kaikkiin toiminnallisuuttasi koskeviin tuotekokouksiin, teet ehdotuksia ja päätöksiä itse. Joka päivä käyttämäsi tuotteen valmistaminen itse on erittäin siistiä, mutta koodin kirjoittaminen ihmisille, jotka ovat siinä luultavasti parempia kuin sinä, on vain uskomaton tunne (ei sarkasmia).

Kaikkien näiden muutosten julkaisun jälkeen saimme positiivista palautetta, ja se oli erittäin, erittäin mukavaa. Se on inspiroivaa. Kiitos! Kirjoita lisää.

Haluan muistuttaa, että globaalien muuttujien jälkeen päätimme muuttaa arkkitehtuuria ja allokoida välityspalvelinkerroksen erilliseen esiintymään. "Kahden solmun" arkkitehtuuri on jo saavuttanut julkaisun julkisen beta-testauksen muodossa. Nyt kuka tahansa voi vaihtaa siihen ja auttaa meitä parantamaan mobiili Habria. Siinä kaikki tältä päivältä. Vastaan ​​mielelläni kaikkiin kysymyksiisi kommenteissa.

Lähde: will.com

Lisää kommentti