Habr front-end fejlesztői naplók: refaktorálás és tükrözés

Habr front-end fejlesztői naplók: refaktorálás és tükrözés

Mindig is érdekelt, hogy a Habr hogyan épül fel belülről, hogyan épül fel a munkafolyamat, hogyan épül fel a kommunikáció, milyen szabványokat használnak és általában hogyan írják ide a kódot. Szerencsére megkaptam ezt a lehetőséget, mert nemrég a habra csapat tagja lettem. A mobil verzió egy kis átalakítása példáján megpróbálok választ adni a kérdésre: milyen itt a fronton dolgozni? A programban: Node, Vue, Vuex és SSR szósszal a Habrban szerzett személyes tapasztalatokról szóló jegyzetekből.

Az első dolog, amit a fejlesztőcsapatról tudni kell, hogy kicsik vagyunk. Kevés három front, két hát és minden Habr - Baxley technikai vezetése. Természetesen van itt tesztelő, tervező, három Vadim, csodaseprű, marketinges és más Bumburumok is. De csak hat közvetlen közreműködő van Habr webhelyein. Ez meglehetősen ritka – egy több millió dolláros közönséggel rendelkező projekt, amely kívülről úgy néz ki, mint egy óriási vállalkozás, valójában inkább egy hangulatos startup, a leglaposabb szervezeti felépítéssel.

Sok más IT-céghez hasonlóan a Habr is agilis ötleteket, CI-gyakorlatokat vall, és ez minden. De véleményem szerint a Habr mint termék inkább hullámokban fejlődik, mint folyamatosan. Így egymás után több sprinten keresztül szorgalmasan kódolunk valamit, tervezünk és újratervezünk, valamit eltörünk és megjavítunk, jegyeket oldunk meg és újakat készítünk, gereblyére lépünk és lábon lőjük magunkat, hogy végre kiadjuk a funkciót a jövő. És akkor jön egy bizonyos elcsendesedés, az újrafejlődés időszaka, ideje megtenni azt, ami a „fontos-nem sürgős” kvadránsban van.

Az alábbiakban pontosan erről a „szezonon kívüli” sprintről lesz szó. Ezúttal a Habr mobil verziójának átdolgozását tartalmazta. Általánosságban elmondható, hogy a cég nagy reményeket fűz hozzá, és a jövőben ki kell váltania Habr inkarnációinak teljes állatkertjét, és egy univerzális, platformok közötti megoldássá kell válnia. Egyszer lesz adaptív elrendezés, PWA, offline mód, felhasználói testreszabás és sok más érdekes dolog.

Tűzzük ki a feladatot

Egyszer egy közönséges stand-up alkalmával az egyik front a mobil verzió megjegyzések összetevőjének architektúrájával kapcsolatos problémákról beszélt. Ezzel a javaslattal mikrotalálkozót szerveztünk csoportos pszichoterápia formájában. Mindenki felváltva mondogatta, hol fáj, mindent papírra vettek, együtt éreztek, megértettek, csakhogy nem tapsolt senki. Az eredmény egy 20 problémát tartalmazó lista lett, amely világossá tette, hogy a mobil Habrnak még mindig hosszú és kanyargós útja van a sikerhez.

Elsősorban az erőforrás-hatékonyság és az úgynevezett sima interfész aggaszt. Minden nap, az „otthon-munka-otthon” útvonalon láttam, hogy a régi telefonom kétségbeesetten próbált 20 főcímet megjeleníteni a hírfolyamban. Valahogy így nézett ki:

Habr front-end fejlesztői naplók: refaktorálás és tükrözésMobile Habr interfész az újrafaktorálás előtt

Mi folyik itt? Röviden: a szerver ugyanúgy szolgálta ki a HTML oldalt mindenkinek, függetlenül attól, hogy a felhasználó bejelentkezett-e vagy sem. Ezután a kliens JS betöltődik, és újra lekéri a szükséges adatokat, de ezúttal az engedélyezéshez igazítva. Vagyis valójában kétszer végeztük el ugyanazt a munkát. A felület villódzott, a felhasználó pedig jó száz plusz kilobájtot töltött le. A részletekben minden még hátborzongatóbbnak tűnt.

Habr front-end fejlesztői naplók: refaktorálás és tükrözésRégi SSR-CSR séma. Az engedélyezés csak a C3 és C4 szakaszban lehetséges, amikor a Node JS nincs elfoglalva a HTML generálásával, és proxy kéréseket tud küldeni az API-hoz.

Az akkori architektúránkat az egyik Habr-felhasználó nagyon pontosan leírta:

A mobil verzió gagyi. Úgy mondom, ahogy van. Az SSR és a CSR szörnyű kombinációja.

Kénytelenek voltunk ezt beismerni, bármilyen szomorú is volt.

Felmértem a lehetőségeket, létrehoztam egy jegyet Jira-ban „most rossz, tedd rendbe” szintű leírással, és nagy vonalakban lebontottam a feladatot:

  • adatok újrafelhasználása,
  • minimalizálja az újrarajzolások számát,
  • az ismétlődő kérések megszüntetése,
  • nyilvánvalóbbá tegye a betöltési folyamatot.

Használjuk újra az adatokat

Elméletileg a szerveroldali megjelenítést két probléma megoldására tervezték: ne szenvedjen a keresőmotorok korlátaitól. SPA indexelés és javítsa a mutatókat FMP (elkerülhetetlenül romlik TTI). A klasszikus forgatókönyv szerint végül 2013-ban fogalmazták meg az Airbnb-n évben (vissza a Backbone.js-en), az SSR ugyanaz az izomorf JS-alkalmazás, amely a Node környezetben fut. A szerver egyszerűen visszaadja a generált elrendezést válaszként a kérésre. Ezután a kliens oldalon történik a folyadékpótlás, majd minden oldal újratöltés nélkül működik. A Habr számára, mint sok más szöveges tartalommal rendelkező erőforrás esetében, a szerverek megjelenítése kritikus eleme a keresőmotorokkal való baráti kapcsolatok kialakításának.

Annak ellenére, hogy több mint hat év telt el a technológia megjelenése óta, és ezalatt az idő alatt sok víz ment el a híd alatt a front-end világban, sok fejlesztő számára ez az ötlet még mindig titokzatos. Nem álltunk félre, és egy SSR-támogatással rendelkező Vue-alkalmazást vezettünk be a gyártásba, egy apró részlet hiányában: nem adtuk át a kezdeti állapotot az ügyfélnek.

Miért? Erre a kérdésre nincs pontos válasz. Vagy nem akarták növelni a szervertől érkező válasz méretét, vagy egy rakás egyéb architektonikus probléma miatt, vagy egyszerűen nem jött be. Így vagy úgy, az állapot kidobása és mindazok újrafelhasználása, amit a szerver csinált, meglehetősen helyénvalónak és hasznosnak tűnik. A feladat valójában triviális. állapotot egyszerűen beadják a végrehajtási kontextusba, és a Vue automatikusan hozzáadja azt a generált elrendezéshez globális változóként: window.__INITIAL_STATE__.

Az egyik felmerülő probléma az, hogy nem lehet ciklikus struktúrákat JSON-ba konvertálni (kör utalás); úgy oldották meg, hogy az ilyen szerkezeteket egyszerűen lecserélték lapos megfelelőire.

Ezenkívül az UGC-tartalom kezelésekor ne feledje, hogy az adatokat HTML-entitásokká kell konvertálni, hogy ne sérüljön meg a HTML. Ezekre a célokra használjuk he.

Az újrarajzolások minimalizálása

Amint a fenti diagramból látható, esetünkben egy Node JS példány két funkciót lát el: SSR-t és egy „proxyt” az API-ban, ahol a felhasználói jogosultság megtörténik. Ez a körülmény lehetetlenné teszi az engedélyezést, amíg a JS-kód fut a kiszolgálón, mivel a csomópont egyszálú, és az SSR funkció szinkron. Vagyis a szerver egyszerűen nem tud kéréseket küldeni magának, miközben a callstack valamivel el van foglalva. Kiderült, hogy frissítettük az állapotot, de a felület nem hagyta abba a rángatózást, mivel a kliens adatait a felhasználói munkamenet figyelembevételével frissíteni kellett. Meg kellett tanítani az alkalmazásunkat, hogy a felhasználó bejelentkezési adatainak figyelembevételével a helyes adatokat helyezze a kezdeti állapotba.

Csak két megoldás volt a problémára:

  • az engedélyezési adatok összekapcsolása kiszolgálók közötti kérésekkel;
  • osztja fel a Node JS rétegeket két külön példányra.

Az első megoldás globális változók használatát tette szükségessé a szerveren, a második pedig legalább egy hónappal meghosszabbította a feladat elvégzésének időkeretét.

Hogyan válasszunk? Habr gyakran halad a legkisebb ellenállás útján. Informálisan általános az a törekvés, hogy az ötlettől a prototípusig terjedő ciklust minimálisra csökkentsék. A termékhez való hozzáállás modellje némileg emlékeztet a booking.com posztulátumaira, azzal a különbséggel, hogy Habr sokkal komolyabban veszi a felhasználói visszajelzéseket, és rád, mint fejlesztőre bízza az ilyen döntéseket.

Ezt a logikát és a probléma gyors megoldására irányuló saját vágyamat követve a globális változókat választottam. És mint ez gyakran megesik, előbb-utóbb fizetni kell értük. Szinte azonnal fizettünk: hétvégén dolgoztunk, tisztáztuk a következményeket, írt halál utáni és elkezdte két részre osztani a szervert. A hiba nagyon hülye volt, és az ezzel járó hibát nem volt könnyű reprodukálni. És igen, kár érte, de így vagy úgy, botladozva és nyögve, a globális változókkal rendelkező PoC-m mégis gyártásba került, és egészen sikeresen működik, amíg várja az új „két csomópontos” architektúrára való átállást. Ez egy fontos lépés volt, mert formálisan a célt sikerült elérni – az SSR megtanult egy teljesen használatra kész oldalt szállítani, és a felhasználói felület is sokkal nyugodtabb lett.

Habr front-end fejlesztői naplók: refaktorálás és tükrözésMobile Habr interfész a refaktorálás első szakasza után

Végül a mobil verzió SSR-CSR architektúrája a következő képhez vezet:

Habr front-end fejlesztői naplók: refaktorálás és tükrözés„Kétcsomópontos” SSR-CSR áramkör. A Node JS API mindig készen áll az aszinkron I/O-ra, és nem blokkolja az SSR függvény, mivel az utóbbi külön példányban található. A 3. lekérdezési lánc nem szükséges.

Az ismétlődő kérések kiküszöbölése

A manipulációk elvégzése után az oldal kezdeti megjelenítése már nem váltott ki epilepsziát. De a Habr további használata SPA módban továbbra is zavart okozott.

Mivel a felhasználói áramlás alapja az űrlap átmenetei cikkek listája → cikk → megjegyzések és fordítva, elsősorban ennek a láncnak az erőforrás-felhasználását volt fontos optimalizálni.

Habr front-end fejlesztői naplók: refaktorálás és tükrözésA bejegyzés hírfolyamához való visszatérés új adatkérést indít el

Nem kellett mélyre ásni. A fenti képernyőn látható, hogy az alkalmazás visszafelé húzáskor újra lekéri a cikkek listáját, és a lekérés során nem látjuk a cikkeket, vagyis a korábbi adatok eltűnnek valahol. Úgy tűnik, hogy a cikklista összetevő helyi állapotot használ, és elveszti a megsemmisítéshez. Valójában az alkalmazás globális állapotot használt, de a Vuex architektúra homlokegyenest felépült: a modulok oldalakhoz vannak kötve, amelyek viszont útvonalakhoz vannak kötve. Ezenkívül az összes modul „egyszeri” - minden további látogatás az oldalra átírta a teljes modult:

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

Összességében volt egy modulunk Cikkek listájatípusú objektumokat tartalmaz Cikk és modul Oldal Cikk, amely az objektum kiterjesztett változata volt Cikk, olyasmi Teljes cikk. Általában véve ez a megvalósítás önmagában nem hordoz semmi szörnyűséget - nagyon egyszerű, akár naivnak is mondható, de rendkívül érthető. Ha minden útvonalváltáskor alaphelyzetbe állítod a modult, akkor akár élni is tudsz vele. Azonban például az átmenet a cikkek hírcsatornái között /feed → /all, garantáltan kidob mindent, ami a személyes hírfolyamoddal kapcsolatos, hiszen nálunk csak egy van Cikkek listája, amelybe új adatokat kell bevinni. Ez ismét a kérések megkettőzéséhez vezet.

Miután összegyűjtöttem mindazt, amit a témában sikerült kiásnom, új államszerkezetet fogalmaztam meg és mutattam be kollégáimnak. A megbeszélések hosszadalmasak voltak, de végül a mellett szóló érvek felülmúlták a kételyeket, és elkezdtem a megvalósítást.

A döntés logikája két szakaszban derül ki a legjobban. Először megpróbáljuk leválasztani a Vuex modult az oldalakról, és közvetlenül az útvonalakhoz kötni. Igen, kicsit több adat lesz a boltban, a getterek kicsit összetettebbek lesznek, de nem töltjük be kétszer a cikkeket. A mobil verziónál talán ez a legerősebb érv. Valahogy így fog kinézni:

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

De mi van akkor, ha a cikkek listái átfedhetnek több útvonal között, és mi van akkor, ha újra szeretnénk használni az objektumadatokat Cikk a bejegyzés oldalának megjelenítéséhez, átalakítva azt Teljes cikk? Ebben az esetben logikusabb lenne egy ilyen szerkezetet használni:

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

Cikkek listája itt csak egyfajta cikktár. Minden cikk, amelyet a felhasználói munkamenet során töltöttek le. A legnagyobb körültekintéssel bánunk velük, mert ez egy olyan forgalom, amelyet esetleg valahol a metróban töltöttek le az állomások között, és semmiképpen sem akarjuk újra ezt a fájdalmat okozni a felhasználónak azzal, hogy a már letöltött adatok betöltésére kényszerítjük. . Egy tárgy Cikkekazonosítók egyszerűen objektumokhoz mutató azonosítók (például „hivatkozások”) tömbje Cikk. Ez a struktúra lehetővé teszi, hogy elkerülje az útvonalakra jellemző adatok megkettőzését és az objektum újrafelhasználását Cikk egy bejegyzés oldalának renderelésekor a kiterjesztett adatok beleolvasztásával.

A cikklista kimenete is átláthatóbbá vált: az iterátor komponens iterál a cikkazonosítókkal ellátott tömbön, és megrajzolja a cikk kedvcsináló komponensét, az azonosítót kellékként átadva, a gyermekkomponens pedig lekéri a szükséges adatokat a cikkazonosítókkal. Cikkek listája. Amikor a publikációs oldalra lép, lekérjük a meglévő dátumot innen Cikkek listája, akkor kérünk, hogy megkapjuk a hiányzó adatokat, és egyszerűen hozzáadjuk a meglévő objektumhoz.

Miért jobb ez a megközelítés? Ahogy fentebb írtam, ez a megközelítés óvatosabb a letöltött adatok tekintetében, és lehetővé teszi azok újrafelhasználását. De ezen kívül utat nyit néhány új lehetőség előtt, amelyek tökéletesen illeszkednek egy ilyen architektúrába. Például lekérdezés és cikkek betöltése a hírfolyamba, ahogy megjelennek. A legfrissebb bejegyzéseket egyszerűen elhelyezhetjük egy „tárhelyen” Cikkek listája, mentse el az új azonosítók külön listáját Cikkekazonosítók és értesítse erről a felhasználót. Amikor az „Új kiadványok megjelenítése” gombra kattintunk, egyszerűen beszúrunk új azonosítókat az aktuális cikklista tömbjének elejére, és minden szinte varázslatosan fog működni.

A letöltés élvezetesebbé tétele

A hab az átalakítási tortán a csontvázak koncepciója, amely egy kicsit kevésbé frusztrálóvá teszi a tartalom betöltésének folyamatát egy lassú interneten. Erről nem volt vita, az ötlettől a prototípusig az út szó szerint két órát vett igénybe. A tervezés gyakorlatilag megrajzolta magát, és megtanítottuk a komponenseinket egyszerű, alig villódzó div blokkok renderelésére adatvárás közben. Szubjektív módon a terhelésnek ez a megközelítése valójában csökkenti a stresszhormonok mennyiségét a felhasználó szervezetében. A csontváz így néz ki:

Habr front-end fejlesztői naplók: refaktorálás és tükrözés
Habraloading

Tükröződés

Hat hónapja dolgozom Habrban, és a barátaim még mindig azt kérdezik: nos, hogy tetszik ott? Oké, kényelmes – igen. De van valami, ami megkülönbözteti ezt a munkát a többiektől. Olyan csapatokban dolgoztam, amelyek teljesen közömbösek voltak a termékük iránt, nem tudták és nem értették, kik a felhasználói. De itt minden más. Itt felelősnek érzi magát azért, amit csinál. A funkció fejlesztése során Ön részben tulajdonossá válik, részt vesz a funkcionalitással kapcsolatos összes terméktalálkozón, javaslatokat tesz és saját maga hoz döntéseket. Nagyon klassz dolog olyan terméket készíteni, amelyet minden nap használ, és kódot írni azoknak, akik valószínűleg jobban értenek hozzá, mint te, hihetetlen érzés (nem szarkazmus).

Mindezen változtatások megjelenése után pozitív visszajelzéseket kaptunk, és ez nagyon-nagyon kedves volt. Ez inspiráló. Köszönöm! Írj még.

Hadd emlékeztesselek arra, hogy a globális változók után úgy döntöttünk, hogy megváltoztatjuk az architektúrát, és a proxy réteget külön példányba foglaljuk. A „két csomópontos” architektúra nyilvános béta tesztelés formájában már megjelent. Most már bárki átválthat rá, és segíthet a mobil Habr fejlesztésében. Ez minden mára. Szívesen válaszolok minden kérdésére a megjegyzésekben.

Forrás: will.com

Hozzászólás