Reconstruirea unui site mare de comerț electronic cu Next.js (studiu de caz)

Publicat: 2022-03-10
Rezumat rapid ↬ Am făcut trecerea de la o platformă de comerț electronic integrată mai tradițională la o platformă fără cap cu Next.js. Iată cele mai importante lecții învățate în timpul reconstrucției unui site mare de comerț electronic cu Next.js.

La compania noastră, Unplatform, construim site-uri de comerț electronic de zeci de ani. De-a lungul acelor ani, am văzut tehnologia evoluând de la pagini redate pe server cu câteva JavaScript și CSS minore la aplicații JavaScript complete.

Platforma pe care am folosit-o pentru site-urile noastre de comerț electronic se baza pe ASP.NET și când vizitatorii au început să se aștepte la mai multă interacțiune, am adăugat React pentru front-end. Deși combinarea conceptelor unui cadru web de server precum ASP.NET cu un cadru web la nivel client precum React a complicat lucrurile, am fost destul de mulțumiți de soluție. Asta până când am intrat în producție cu clientul nostru cu cel mai mare trafic. Din momentul în care am lansat live, ne-am confruntat cu probleme de performanță . Core Web Vitals sunt importante, cu atât mai mult în comerțul electronic. În acest studiu Deloitte: Milliseconds Make Millions, anchetatorii au analizat datele site-urilor mobile ale a 37 de mărci diferite. Drept urmare, au descoperit că o îmbunătățire a performanței cu 0,1 s poate duce la o creștere cu 10% a conversiei.

Pentru a atenua problemele de performanță, a trebuit să adăugăm o mulțime de servere suplimentare (nebugetate) și a trebuit să memorăm în cache paginile într-un proxy invers. Acest lucru ne-a impus chiar să dezactivăm părți ale funcționalității site-ului. Am ajuns să avem o soluție foarte complicată și costisitoare, care în unele cazuri doar a servit static unele pagini.

Evident, acest lucru nu a fost corect, până când am aflat despre Next.js. Next.js este un cadru web bazat pe React, care vă permite să generați pagini static, dar puteți utiliza în continuare randarea pe partea serverului, ceea ce îl face ideal pentru comerțul electronic. Poate fi găzduit pe un CDN precum Vercel sau Netlify, ceea ce duce la o latență mai mică . Vercel și Netlify folosesc, de asemenea, funcții fără server pentru redarea pe partea serverului, care este cea mai eficientă modalitate de a scala.

Provocări

Dezvoltarea cu Next.js este uimitoare, dar cu siguranță există unele provocări. Experiența dezvoltatorului cu Next.js este ceva ce trebuie doar să experimentați. Codul pe care îl scrieți se vizualizează instantaneu în browser și productivitatea trece prin cer. Acesta este, de asemenea, un risc, deoarece vă puteți concentra cu ușurință prea mult pe productivitate și puteți neglija mentenabilitatea codului dvs. În timp, aceasta și natura netipificată a JavaScript poate duce la degradarea bazei de cod. Numărul de erori crește și productivitatea începe să scadă.

Poate fi o provocare și din punctul de vedere al timpului de execuție . Cele mai mici modificări ale codului dvs. pot duce la o scădere a performanței și la alte elemente vitale de bază ale web. De asemenea, utilizarea neglijentă a redării pe server poate duce la costuri neașteptate ale serviciilor.

Să aruncăm o privire mai atentă la lecțiile noastre învățate în depășirea acestor provocări.

  1. Modularizează-ți baza de cod
  2. Lint și formatați-vă codul
  3. Utilizați TypeScript
  4. Planificați performanța și măsurați performanța
  5. Adăugați verificări de performanță la poarta dvs. de calitate
  6. Adăugați teste automate
  7. Gestionați-vă agresiv dependențele
  8. Utilizați un serviciu de agregare a jurnalelor
  9. Funcționalitatea de rescriere a Next.js permite adoptarea incrementală
Mai multe după săritură! Continuați să citiți mai jos ↓

Lecția învățată: Modularizează-ți baza de cod

Framework-urile front-end, cum ar fi Next.js, fac atât de ușor să începeți în zilele noastre. Pur și simplu rulați npx create-next-app și puteți începe să codificați. Dar dacă nu ești atent și începi să scoți cod fără să te gândești la design, s-ar putea să ajungi cu o minge mare de noroi.

Când rulați npx create-next-app , veți avea o structură de foldere ca următoarea (așa sunt, de asemenea, structurate majoritatea exemplelor):

 /public logo.gif /src /lib /hooks useForm.js /api content.js /components Header.js Layout.js /pages Index.js

Am început să folosim aceeași structură. Aveam câteva subfoldere în folderul componente pentru componente mai mari, dar majoritatea componentelor se aflau în folderul componente rădăcină. Nu este nimic în neregulă cu această abordare și este bine pentru proiecte mai mici. Cu toate acestea, pe măsură ce proiectul nostru a crescut, a devenit mai greu să raționăm despre componente și unde sunt utilizate. Am găsit chiar și componente care nu mai erau deloc folosite! De asemenea, promovează o minge mare de noroi, deoarece nu există îndrumări clare cu privire la codul care ar trebui să depindă de ce alt cod.

Pentru a rezolva acest lucru, am decis să refactorăm baza de cod și să grupăm codul după module funcționale (un fel ca modulele NPM) în loc de concepte tehnice:

 /src /modules /catalog /components productblock.js /checkout /api cartservice.js /components cart.js

În acest mic exemplu, există un modul de plată și un modul de catalog. Gruparea codului în acest fel duce la o mai bună descoperire: doar privind structura folderului știți exact ce fel de funcționalitate este în baza de cod și unde să o găsiți. De asemenea, face mult mai ușor să raționezi despre dependențe . În situația anterioară, existau o mulțime de dependențe între componente. Am avut solicitări de tragere pentru modificări în finalizarea achiziției care au afectat și componentele catalogului. Acest lucru a crescut numărul de conflicte de îmbinare și a făcut mai dificilă efectuarea modificărilor.

Soluția care a funcționat cel mai bine pentru noi a fost să menținem dependențele dintre module la un minim absolut (dacă într-adevăr aveți nevoie de o dependență, asigurați-vă că este unidirecțională) și să introducem un nivel de „proiect” care leagă totul:

 /src /modules /common /atoms /lib /catalog /components productblock.js /checkout /api cartservice.js /components cart.js /search /project /layout /components /templates productdetail.js cart.js /pages cart.js

O prezentare vizuală a acestei soluții:

O prezentare generală a unui exemplu de proiect modularizat
O prezentare generală a unui exemplu de proiect modular (Previzualizare mare)

Nivelul de proiect conține codul pentru aspectul site-ului de comerț electronic și șabloane de pagină. În Next.js, o componentă de pagină este o convenție și are ca rezultat o pagină fizică. Din experiența noastră, aceste pagini trebuie adesea să refolosească aceeași implementare și de aceea am introdus conceptul de „șabloane de pagină”. Șabloanele de pagină folosesc componentele din diferite module, de exemplu, șablonul de pagină cu detalii despre produs va folosi componente din catalog pentru a afișa informații despre produs, dar și o componentă de adăugare în coș din modulul de finalizare a comenzii.

Avem, de asemenea, un modul comun, deoarece există încă ceva cod care trebuie reutilizat de modulele funcționale. Conține atomi simpli care sunt componente React utilizate pentru a oferi un aspect și o senzație consistente. De asemenea, conține cod de infrastructură, gândiți-vă la anumite cârlige de reacție generice sau la codul client GraphQL.

Avertisment : Asigurați-vă că codul din modulul comun este stabil și gândiți-vă întotdeauna de două ori înainte de a adăuga cod aici, pentru a preveni încurcarea codului.

Micro Front-End-uri

În soluții și mai mari sau când lucrați cu echipe diferite, poate avea sens să împărțiți aplicația și mai mult în așa-numitele micro-frontend-uri. Pe scurt, aceasta înseamnă împărțirea aplicației și mai mult în mai multe aplicații fizice care sunt găzduite independent pe adrese URL diferite. De exemplu: checkout.mydomain.com și catalog.mydomain.com. Acestea sunt apoi integrate de o aplicație diferită care acționează ca un proxy.

Funcționalitatea de rescriere a Next.js este excelentă pentru aceasta și utilizarea acesteia în acest fel este acceptată de așa-numitele Multi Zones.

Un exemplu de configurare cu mai multe zone
Un exemplu de configurare cu mai multe zone (previzualizare mare)

Avantajul multizonelor este că fiecare zonă își gestionează propriile dependențe. De asemenea, face mai ușoară evoluția progresivă a bazei de cod: dacă apare o nouă versiune Next.js sau React, puteți actualiza zonele una câte una în loc să trebuiască să actualizați întreaga bază de cod simultan. Într-o organizație cu mai multe echipe, acest lucru poate reduce foarte mult dependențele dintre echipe.

Lectură suplimentară

  • „Structura proiectului Next.js”, Yannick Wittwer, mediu
  • „Un ghid pentru 2021 despre structurarea proiectului Next.js într-un mod flexibil și eficient”, Vadorequest, Dev.to.
  • „Micro Frontend-uri”, Michael Geers

Lecția învățată: scame și formatați-vă codul

Acesta este ceva ce am învățat într-un proiect anterior: dacă lucrați în aceeași bază de cod cu mai multe persoane și nu utilizați un formatator, codul dvs. va deveni în curând foarte inconsecvent. Chiar dacă utilizați convenții de codare și faceți recenzii, în curând veți începe să observați diferitele stiluri de codare, dând o impresie dezordonată codului.

Un linter vă va verifica codul pentru probleme potențiale, iar un formatator se va asigura că codul este formatat într-un mod consecvent. Folosim ESLint & Prettier și considerăm că sunt minunate. Nu trebuie să vă gândiți la stilul de codare, reducând încărcătura cognitivă în timpul dezvoltării.

Din fericire, Next.js 11 acceptă acum ESLint din cutie (https://nextjs.org/blog/next-11), făcându-l foarte ușor de configurat prin rularea npx next lint. Acest lucru vă economisește mult timp, deoarece vine cu o configurație implicită pentru Next.js. De exemplu, este deja configurat cu o extensie ESLint pentru React. Și mai bine, vine cu o nouă extensie specifică Next.js, care va identifica chiar probleme cu codul dvs., care ar putea avea un impact potențial asupra corespondenței web vitale ale aplicației dvs.! Într-un paragraf ulterior, vom vorbi despre porțile de calitate care vă pot ajuta să preveniți împingerea codului către un produs care vă dăunează accidental Core Web Vitals. Această extensie vă oferă feedback mult mai rapid, ceea ce o face o completare grozavă.

Lectură suplimentară

  • „ESLint”, Next.js Docs
  • „ESLint”, site-ul oficial

Lecția învățată: Utilizați TypeScript

Pe măsură ce componentele au fost modificate și refactorizate, am observat că unele dintre elementele de recuzită ale componentelor nu mai erau folosite. De asemenea, în unele cazuri, am întâmpinat erori din cauza unor tipuri de elemente de recuzită lipsă sau incorecte care au fost trecute în componente.

TypeScript este un superset de JavaScript și adaugă tipuri, ceea ce permite unui compilator să verifice static codul dvs., un fel ca un linter pe steroizi.

La începutul proiectului, nu am văzut cu adevărat valoarea adăugării TypeScript. Am simțit că este doar o abstracție inutilă. Cu toate acestea, unul dintre colegii noștri a avut experiențe bune cu TypeScript și ne-a convins să încercăm. Din fericire, Next.js are un suport excelent pentru TypeScript, iar TypeScript vă permite să îl adăugați la soluția dvs. treptat. Aceasta înseamnă că nu trebuie să rescrieți sau să vă convertiți întreaga bază de cod dintr-o singură mișcare, dar puteți începe să o utilizați imediat și să convertiți încet restul bazei de cod.

Odată ce am început migrarea componentelor către TypeScript, am găsit imediat probleme cu valori greșite care au fost transmise în componente și funcții. De asemenea, bucla de feedback a dezvoltatorului s-a scurtat și sunteți notificat despre probleme înainte de a rula aplicația în browser. Un alt mare beneficiu pe care l-am găsit este că face mult mai ușoară refactorizarea codului: este mai ușor să vezi unde este folosit codul și descoperi imediat elementele de recuzită și codul neutilizate ale componentelor. Pe scurt, beneficiile TypeScript:

  1. Reduce numărul de bug-uri
  2. Ușurează refactorizarea codului
  3. Codul devine mai ușor de citit

Lectură suplimentară

  • „TypeScript”, Next.js Docs
  • TypeScript, site-ul oficial

Lecția învățată: Planificați performanța și măsurați performanța

Next.js acceptă diferite tipuri de pre-rendare: generare statică și randare pe server. Pentru o performanță optimă, se recomandă utilizarea generației statice, care se întâmplă în timpul construirii, dar acest lucru nu este întotdeauna posibil. Gândiți-vă la paginile cu detalii despre produse care conțin informații despre stoc. Acest tip de informații se schimbă des și rularea unei versiuni de fiecare dată nu se scalează bine. Din fericire, Next.js acceptă și un mod numit Regenerare statică incrementală (ISR), care încă generează pagina static, dar generează una nouă în fundal la fiecare x secunde. Am aflat că acest model funcționează excelent pentru aplicații mai mari. Performanța este încă grozavă, necesită mai puțin timp CPU decât randarea pe server și reduce timpul de construcție: paginile sunt generate doar la prima solicitare. Pentru fiecare pagină pe care o adăugați, ar trebui să vă gândiți la tipul de randare necesar. Mai întâi, vezi dacă poți folosi generarea statică; dacă nu, alegeți regenerarea statică incrementală și, dacă nici asta nu este posibil, puteți utiliza în continuare randarea pe server.

Next.js determină automat tipul de randare pe baza absenței metodelor getServerSideProps și getInitialProps de pe pagină. Este ușor să faci o greșeală, ceea ce ar putea face ca pagina să fie redată pe server în loc să fie generată static. Ieșirea unei versiuni Next.js arată exact ce pagină folosește ce tip de randare, așa că asigurați-vă că verificați acest lucru. De asemenea, ajută la monitorizarea producției și la urmărirea performanței paginilor și a timpului CPU implicat. Majoritatea furnizorilor de găzduire vă taxează în funcție de timpul procesorului și acest lucru ajută la prevenirea oricăror surprize neplăcute. Voi descrie modul în care monitorizăm acest lucru în Lecția învățată: Utilizați un paragraf al serviciului de agregare a jurnalelor.

Dimensiunea pachetului

Pentru a avea o performanță bună, este esențial să minimizați dimensiunea pachetului. Next.js are o mulțime de funcții din cutie care ajută, de exemplu, împărțirea automată a codului. Acest lucru se va asigura că numai JavaScript și CSS necesare sunt încărcate pentru fiecare pagină. De asemenea, generează diferite pachete pentru client și pentru server. Cu toate acestea, este important să fii cu ochii pe acestea. De exemplu, dacă importați module JavaScript într-un mod greșit, serverul JavaScript poate ajunge în pachetul de clienți, mărind foarte mult dimensiunea pachetului de clienți și dăunând performanței. Adăugarea de dependențe NPM poate avea un impact semnificativ asupra dimensiunii pachetului.

Din fericire, Next.js vine cu un analizor de pachete care vă oferă o perspectivă asupra codului care ocupă ce parte a pachetelor.

Analizatorul de pachete webpack vă arată dimensiunea pachetelor din pachetul dvs
Analizatorul de pachete webpack vă arată dimensiunea pachetelor din pachetul dvs. (previzualizare mare)

Lectură suplimentară

  • „Next.js + Analizor de pachete Webpack”, Vercel, GitHub
  • „Preluarea datelor”, Next.js Docs

Lecția învățată: Adăugați verificări de performanță la poarta dvs. de calitate

Unul dintre marile beneficii ale utilizării Next.js este capacitatea de a genera pagini static și de a putea implementa aplicația la margine (CDN), ceea ce ar trebui să aibă ca rezultat o performanță excelentă și vitale web. Am învățat că, chiar și cu o tehnologie excelentă precum Next.js, obținerea și păstrarea unui scor excelent este foarte greu. S-a întâmplat de câteva ori ca, după ce am implementat unele modificări în producție, scorul farului a scăzut semnificativ. Pentru a prelua controlul din nou, am adăugat teste automate de far la poarta noastră de calitate. Cu această acțiune Github, puteți adăuga automat teste far la solicitările dvs. de extragere. Folosim Vercel și de fiecare dată când este creată o cerere de extragere, Vercel o implementează la o adresă URL de previzualizare și folosim acțiunea Github pentru a rula teste far pentru această implementare.

Un exemplu de far rezultă într-o cerere Github Pull Request
Un exemplu de rezultate ale farului la o solicitare Github Pull (previzualizare mare)

Dacă nu doriți să configurați singur acțiunea GitHub sau dacă doriți să duceți acest lucru și mai departe, puteți lua în considerare și un serviciu de monitorizare a performanței terță parte, cum ar fi DebugBear. Vercel oferă, de asemenea, o funcție Analytics, care măsoară elementele vitale web de bază ale implementării dvs. de producție. Vercel Analytics colectează efectiv măsurile de pe dispozitivele vizitatorilor dvs., astfel încât aceste scoruri sunt cu adevărat ceea ce se confruntă vizitatorii dvs. La momentul redactării acestui articol, Vercel Analytics funcționează numai pe implementări de producție.

Lecție învățată: Adăugați teste automate

Când baza de cod devine mai mare, devine mai greu de determinat dacă modificările codului ar fi putut strica funcționalitatea existentă. Din experiența noastră, este vital să avem un set bun de teste end-to-end ca plasă de siguranță. Chiar dacă ai un proiect mic, îți poate face viața mult mai ușoară atunci când ai măcar câteva teste de bază de fum. Am folosit Cypress pentru asta și ne place absolut. Combinația dintre utilizarea Netlify sau Vercel pentru a implementa automat cererea dvs. Pull într-un mediu temporar și rularea testelor dvs. E2E este neprețuită.

Folosim cypress-io/GitHub-action pentru a rula automat testele cypress împotriva solicitărilor noastre de extragere. În funcție de tipul de software pe care îl construiți, poate fi valoros să aveți și teste mai granulare folosind Enzyme sau JEST. Compensația este că acestea sunt mai strâns legate de codul dvs. și necesită mai multă întreținere.

Un exemplu de verificări automate pentru o solicitare Github Pull
Un exemplu de verificări automate pentru o solicitare Github Pull (previzualizare mare)

Lecția învățată: Gestionați-vă agresiv dependențele

Gestionarea dependențelor devine o activitate consumatoare de timp, dar atât de importantă atunci când mențineți o bază de cod Next.js mare. NPM a făcut să adăugați pachete atât de ușor și pare să existe un pachet pentru toate zilele acestea. Privind în urmă, de multe ori când am introdus un nou bug sau am avut o scădere a performanței, a avut ceva de-a face cu un pachet NPM nou sau actualizat.

Deci, înainte de a instala un pachet, ar trebui să vă întrebați întotdeauna următoarele:

  • Care este calitatea pachetului?
  • Ce va însemna adăugarea acestui pachet pentru dimensiunea pachetului meu?
  • Este cu adevărat necesar acest pachet sau există alternative?
  • Pachetul este încă întreținut activ?

Pentru a menține dimensiunea pachetului mică și pentru a minimiza efortul necesar pentru a menține aceste dependențe, este important să păstrați numărul de dependențe cât mai mic posibil. Eul tău viitor vă va mulțumi pentru asta atunci când întrețineți software-ul.

Sfat : Extensia Import Cost VSCode arată automat dimensiunea pachetelor importate.

Fii la curent cu versiunile Next.js

Este important să ții pasul cu Next.js & React. Nu numai că vă va oferi acces la noi funcții, dar noile versiuni vor include și remedieri de erori și remedieri pentru potențiale probleme de securitate. Din fericire, Next.js face actualizarea incredibil de ușoară, oferind Codemods (https://nextjs.org/docs/advanced-features/codemods. Acestea sunt transformări automate de cod care vă actualizează automat codul.

Actualizați dependențe

Din același motiv, este important să păstrați actuale versiunile Next.js și React; de asemenea, este important să actualizați alte dependențe. Dependabot-ul lui Github (https://github.com/dependabot) poate ajuta cu adevărat aici. Va crea automat cereri de tragere cu dependențe actualizate. Cu toate acestea, actualizarea dependențelor poate distruge lucrurile, astfel încât să existe teste automate end-to-end aici poate fi cu adevărat o salvare.

Lecție învățată: Utilizați un serviciu de agregare a jurnalelor

Pentru a ne asigura că aplicația se comportă corect și pentru a găsi în mod preventiv probleme, am constatat că este absolut necesar să configurați un serviciu de agregare a jurnalelor. Vercel vă permite să vă conectați și să vizualizați jurnalele, dar acestea sunt transmise în flux în timp real și nu sunt persistente. De asemenea, nu acceptă configurarea alertelor și notificărilor.

Unele excepții pot dura mult timp să apară. De exemplu, am configurat Stale-While-Revalidate pentru o anumită pagină. La un moment dat, am observat că paginile nu erau reîmprospătate și că erau difuzate date vechi. După verificarea înregistrării Vercel, am constatat că a avut loc o excepție în timpul redării în fundal a paginii. Utilizând un serviciu de agregare a jurnalelor și configurând o alertă pentru excepții, am fi putut detecta acest lucru mult mai devreme.

Serviciile de agregare a jurnalelor pot fi utile și pentru a monitoriza limitele planurilor de prețuri Vercel. Pagina de utilizare a Vercel vă oferă și perspective în acest sens, dar utilizarea unui serviciu de agregare a jurnalelor vă permite să adăugați notificări atunci când atingeți un anumit prag. Este mai bine să previi decât să vindeci, mai ales când vine vorba de facturare.

Vercel oferă o serie de integrări gata de fabricație cu servicii de agregare a jurnalelor, incluzând Datadog, Logtail, Logalert, Sentry și multe altele.

Vizualizarea jurnalului de solicitare Next.js în Datadog
Vizualizarea jurnalului de solicitare Next.js în Datadog (previzualizare mare)

Lectură suplimentară

  • „Integrații”, Vercel

Lecție învățată: funcționalitatea de rescriere a Next.js permite adoptarea incrementală

Dacă nu există probleme serioase cu site-ul actual, nu mulți clienți vor fi încântați să rescrie întregul site. Dar ce se întâmplă dacă ai putea începe cu reconstruirea numai a paginilor care contează cel mai mult din punct de vedere vital al web? Este exact ceea ce am făcut pentru un alt client. În loc să reconstruim întregul site, reconstruim doar paginile care contează cel mai mult pentru SEO și conversie. În acest caz, paginile cu detalii despre produs și categorii. Prin reconstruirea celor cu Next.js, performanța a crescut foarte mult.

Funcționalitatea de rescriere Next.js este excelentă pentru asta. Am construit un nou front-end Next.js care conține paginile de catalog și l-am implementat pe CDN. Toate celelalte pagini existente sunt rescrise de Next.js pe site-ul web existent. În acest fel, puteți începe să beneficiați de beneficiile unui site Next.js într-un mod cu efort redus sau cu risc scăzut.

Lectură suplimentară

  • „Rescrie”, Next.js Docs

Ce urmeaza?

Când am lansat prima versiune a proiectului și am început să facem teste serioase de performanță, am fost încântați de rezultate. Nu numai că timpii de răspuns la pagină și Web Vitals au fost mult mai buni decât înainte, dar costurile operaționale au fost, de asemenea, o fracțiune din ceea ce erau înainte. Next.js și JAMStack vă permit în general să extindeți în cel mai eficient mod din punct de vedere al costurilor.

Trecerea de la o arhitectură mai orientată spre back-end la ceva de genul Next.js este un pas mare. Curba de învățare poate fi destul de abruptă și, inițial, unii membri ai echipei s-au simțit cu adevărat în afara zonei lor de confort. Micile ajustări pe care le-am făcut, lecțiile învățate din acest articol, ne-au ajutat cu adevărat în acest sens. De asemenea, experiența de dezvoltare cu Next.js oferă o creștere uimitoare a productivității. Ciclul de feedback al dezvoltatorului este incredibil de scurt!

Lectură suplimentară

  • „Merg în producție”, Next.js Docs