Punctele forte și beneficiile micro-frontend-urilor
Publicat: 2022-03-11Arhitectura micro-frontend este o abordare de proiectare în care o aplicație front-end este descompusă în „microaplicații” individuale, semi-independente, care lucrează liber împreună. Conceptul de micro-frontend este vag inspirat de microservicii și poartă numele după acestea.
Beneficiile modelului micro-frontend includ:
- Arhitecturile micro-frontend pot fi mai simple și, prin urmare, mai ușor de raționat și gestionat.
- Echipele independente de dezvoltare pot colabora mai ușor la o aplicație front-end.
- Ele pot oferi un mijloc de migrare dintr-o aplicație „veche”, având o aplicație „nouă” care rulează alături de ea.
Deși micro-frontend-urile au atras multă atenție în ultima vreme, până în prezent nu există o implementare dominantă unică și nici un cadru de micro-frontend clar „cel mai bun”. De fapt, există o varietate de abordări în funcție de obiective și cerințe. Consultați bibliografia pentru unele dintre cele mai cunoscute implementări.
În acest articol, vom sări peste o mare parte din teoria micro frontend-urilor. Iată ce nu vom acoperi:
- „Tăierea” unei aplicații în microaplicații
- Probleme de implementare, inclusiv modul în care micro frontend-urile se potrivesc într-un model CI/CD
- Testare
- Dacă microaplicațiile ar trebui să fie aliniate unu-la-unu cu microservicii de pe backend
- Critici ale conceptului de micro-frontend
- Diferența dintre micro frontend-uri și o arhitectură simplă a componentelor veche
În schimb, vom prezenta un tutorial micro-frontend axat pe o implementare concretă, evidențiind problemele importante din arhitectura micro-frontend și posibilele soluții ale acestora.
Implementarea noastră se numește Yumcha. Sensul literal al „yum cha” în cantoneză este „a bea ceai”, dar sensul său de zi cu zi este „a ieși pentru dim sum”. Ideea aici este că microaplicațiile individuale dintr-o macroaplicație (așa cum vom numi aplicația compusă, de nivel superior) sunt analoge cu diferitele coșuri de porții de dimensiuni mici, scoase la iveală la un prânz de dim sum.
Ne vom referi uneori la Yumcha drept „cadru micro-frontend”. În lumea de astăzi, termenul „cadru” este de obicei folosit pentru a se referi la Angular, React, Vue.js sau alte suprastructuri similare pentru aplicațiile web. Nu vorbim deloc despre un cadru în acest sens. Numim Yumcha un cadru doar de dragul confortului: este de fapt mai mult un set de instrumente și câteva straturi subțiri pentru construirea de aplicații bazate pe micro-frontend.
Tutorial micro-frontend Primii pași: Markup pentru o aplicație compusă
Să ne aprofundăm gândindu-ne la modul în care am putea defini o macroaplicație și microaplicațiile care o compun. Markupul a fost întotdeauna în centrul webului. Macroaplicația noastră nu va fi, așadar, specificată de nimic mai complicat decât acest marcaj:
<html> <head> <script src="/yumcha.js"></script> </head> <body> <h1>Hello, micro-frontend app.</h1> <!-- HERE ARE THE MICROAPPS! --> <yumcha-portal name="microapp1" src="https://microapp1.example.com"></yumcha-portal> <yumcha-portal name="microapp2" src="https://microapp2.example.com"></yumcha-portal> </body> </html>
Definirea macroaplicației folosind marcajul ne oferă acces deplin la puterea HTML și CSS de a așeza și gestiona microaplicațiile noastre. De exemplu, o microaplicație s-ar putea să stea deasupra alteia, sau pe o parte, sau să fie sus în colțul paginii, sau să fie într-un panou al unui acordeon sau să rămână ascunsă până când se întâmplă ceva sau să rămână în fundal permanent .
Am denumit elementul personalizat folosit pentru microaplicații <yumcha-portal>
deoarece „portal” este un termen promițător pentru microaplicații utilizat în propunerea portalului, o încercare timpurie de a defini un element HTML standard pentru utilizare în micro-interface-uri.
Implementarea elementului personalizat <yumcha-portal>
Cum ar trebui să implementăm <yumcha-portal>
? Deoarece este un element personalizat, ca componentă web, desigur! Putem alege dintr-un număr de concurenți puternici pentru scrierea și compilarea componentelor web micro-frontend; aici vom folosi LitElement, cea mai recentă iterație a Proiectului Polymer. LitElement acceptă zahăr sintactic bazat pe TypeScript, care se ocupă de majoritatea elementelor personalizate pentru noi. Pentru a face <yumcha-portal>
disponibil pentru pagina noastră, trebuie să includem codul relevant ca <script>
, așa cum am făcut mai sus.
Dar ce face <yumcha-portal>
? O primă aproximare ar fi ca acesta să creeze doar un iframe
cu sursa specificată:
render() { return html`<iframe src=${this.src}></iframe>`; }
…unde render
este cârligul standard de randare LitElement, folosind literalul șablonului etichetat html
. Această funcționalitate minimă ar putea fi aproape suficientă pentru unele cazuri de utilizare triviale.
Încorporarea microaplicațiilor în iframe
s
iframe
-urile sunt elementul HTML pe care toată lumea iubește să-l urască, dar de fapt oferă un comportament sandboxing extrem de util, solid. Cu toate acestea, există încă o listă lungă de probleme de care trebuie să fiți conștienți atunci când utilizați iframe
-uri, cu impact potențial asupra comportamentului și funcționalității aplicației noastre:
- În primul rând,
iframe
au particularități binecunoscute în ceea ce privește modul în care se dimensionează și se așează. - CSS va fi, desigur, complet izolat de
iframe
, la bine sau la rău. - Butonul „înapoi” al browserului va funcționa destul de bine, deși starea curentă de navigare a
iframe
-ului nu se va reflecta în adresa URL a paginii , așa că nu am putea nici tăia și lipi URL-uri pentru a ajunge la aceeași stare a aplicației compuse, nici link-ul profund. lor. - Comunicarea cu
iframe
din exterior, în funcție de configurația noastră CORS, ar putea trebui să treacă prin protocolulpostMessage
. - Vor trebui făcute aranjamente pentru autentificare peste granițele
iframe
. - Unele cititoare de ecran se pot împiedica de limita
iframe
-ului sau pot avea nevoie caiframe
-ul să aibă un titlu pe care îl pot anunța utilizatorului.
Unele dintre aceste probleme pot fi evitate sau atenuate prin iframe
-urilor, o alternativă pe care o discutăm mai târziu în articol.
În plus, iframe
-ul va avea propria Content-Security-Policy
(CSP). De asemenea, dacă microaplicația către care indică iframe
folosește un service worker sau implementează randarea pe server, totul va funcționa conform așteptărilor. De asemenea, putem specifica diverse opțiuni de sandboxing pentru iframe
pentru a-i limita capacitățile, cum ar fi posibilitatea de a naviga la cadrul superior.
Unele browsere au livrat sau intenționează să livreze un atribut loading=lazy
pentru iframe
-uri, care amână încărcarea iframe
-urilor de sub fold până când utilizatorul defilează lângă ele, dar acest lucru nu oferă controlul fin al încărcării leneșe care noi vrem.
Adevărata problemă cu iframe
s este că conținutul iframe
-ului va prelua mai multe solicitări de rețea. index.html
de nivel superior este primit, scripturile sale sunt încărcate și HTML-ul său este parsat, dar apoi browserul trebuie să inițieze o altă solicitare pentru HTML-ul iframe
-ului, să aștepte să-l primească, să analizeze și să încarce scripturile sale și să redea conținutul iframe
. În multe cazuri, JavaScript-ul iframe
-ului ar trebui să se rotească, să facă propriile apeluri API și să arate date semnificative numai după ce apelurile API revin și datele sunt procesate pentru vizualizare.
Acest lucru va duce probabil la întârzieri nedorite și artefacte de redare, mai ales atunci când sunt implicate mai multe microaplicații. Dacă aplicația iframe
implementează SSR, asta va ajuta, dar nu va evita necesitatea unor călătorii dus-întors suplimentare.
Așadar, una dintre provocările cheie cu care ne confruntăm în proiectarea implementării portalului nostru este cum să facem față acestei probleme dus-întors. Scopul nostru este ca o singură solicitare de rețea să reducă întreaga pagină cu toate microaplicațiile sale, inclusiv orice conținut pe care fiecare dintre ele este capabil să prepopuleze. Soluția la această problemă constă în serverul Yumcha.
Serverul Yumcha
Un element cheie al soluției micro-frontend prezentate aici este configurarea unui server dedicat pentru a gestiona compoziția microaplicațiilor. Acest server trimite cererile către serverele pe care este găzduită fiecare microaplicație. Desigur, va fi nevoie de ceva efort pentru a configura și gestiona acest server. Unele abordări micro-frontend (de exemplu, single-spa) încearcă să renunțe la necesitatea unor astfel de setări speciale de server, în numele ușurinței implementării și configurării.
Cu toate acestea, costul creării acestui proxy invers este mai mult decât compensat de beneficiile pe care le obținem; de fapt, există comportamente importante ale aplicațiilor bazate pe micro frontend pe care pur și simplu nu le putem realiza fără ele. Există multe alternative comerciale și gratuite pentru a configura un astfel de proxy invers.
Proxy-ul invers, pe lângă direcționarea cererilor de microaplicații către serverul corespunzător, direcționează și cererile de macroaplicații către un server de macroaplicații. Serverul respectiv trimite codul HTML pentru aplicația compusă într-un mod special. La primirea unei cereri pentru index.html
din browser prin intermediul serverului proxy la o adresă URL cum ar fi http://macroapp.example.com
, preia index.html
și apoi îl supune unei transformări simple, dar cruciale înainte de a reveni. aceasta.
Mai exact, HTML-ul este analizat pentru <yumcha-portal>
, ceea ce se poate face cu ușurință cu unul dintre analizoarele HTML competente disponibile în ecosistemul Node.js. Folosind atributul src
la <yumcha-portal>
, serverul care rulează microaplicația este contactat și index.html
-ul acestuia este preluat, inclusiv conținutul randat pe server, dacă există. Rezultatul este inserat în răspunsul HTML ca etichetă <script>
sau <template>
, pentru a nu fi executat de browser.
Avantajele acestei configurații includ, în primul rând, că la prima solicitare a index.html
pentru pagina compusă, serverul poate prelua paginile individuale de pe serverele individuale de microaplicații în întregime — inclusiv conținutul redat prin SSR, dacă oricare — și să livreze browserului o singură pagină completă, inclusiv conținutul care poate fi folosit pentru a popula iframe
-ul fără călătorii dus-întors la server suplimentare (folosind atributul srcdoc
subutilizat). Serverul proxy se asigură, de asemenea, că orice detalii despre unde sunt servite microaplicațiile sunt ascunse de privirile indiscrete. În cele din urmă, simplifică problemele CORS, deoarece cererile de aplicații sunt făcute toate la aceeași origine.

Înapoi la client, <yumcha-portal>
este instanțiată și găsește conținutul unde a fost plasat în documentul de răspuns de către server și, la momentul potrivit, redă iframe
-ul și atribuie conținutul atributului său srcdoc
. Dacă nu folosim iframe
s (vezi mai jos), atunci conținutul corespunzător <yumcha-portal>
este inserat fie în DOM-ul umbră al elementului personalizat, dacă îl folosim, fie direct în linie în document.
În acest moment, avem deja o aplicație bazată pe micro frontend care funcționează parțial.
Acesta este doar vârful aisbergului în ceea ce privește funcționalitatea interesantă pentru serverul Yumcha. De exemplu, am dori să adăugăm funcții pentru a controla modul în care sunt gestionate răspunsurile de eroare HTTP de la serverele de microaplicații sau cum să tratăm cu microaplicațiile care răspund foarte lent - nu vrem să așteptăm mereu pentru a difuza pagina dacă o microaplicație nu este raspunde! Acestea și alte subiecte le vom lăsa pentru o altă postare.
Logica de transformare Yumcha macroapp index.html
ar putea fi implementată cu ușurință într-o funcție lambda fără server sau ca middleware pentru cadre de server precum Express sau Koa.
Control microaplicație bazat pe stub
Revenind la partea clientului, există un alt aspect al modului în care implementăm microaplicațiile care este important pentru eficiență, încărcare leneșă și randare fără șocuri. Am putea genera eticheta iframe
pentru fiecare microaplicație, fie cu un atribut src
- care face o altă solicitare de rețea - sau cu atributul srcdoc
completat cu conținutul populat pentru noi de server. Dar, în ambele cazuri, codul din acel iframe
va începe imediat, inclusiv încărcarea tuturor etichetelor de script și link, bootstrapping și orice apeluri API inițiale și procesarea datelor asociate - chiar dacă utilizatorul nu accesează niciodată microaplicația în cauză.
Soluția noastră la această problemă este să reprezentăm inițial microaplicațiile pe pagină ca niște stub-uri minuscule inactivate, care pot fi apoi activate. Activarea poate fi determinată fie de intrarea în vedere a regiunii microaplicației, folosind API-ul IntersectionObserver
subutilizat, fie, mai frecvent, de notificări prealabile trimise din exterior. Desigur, putem specifica și ca microaplicația să fie activată imediat.
În orice caz, când și numai atunci când microaplicația este activată, iframe
-ul este redat efectiv și codul său este încărcat și executat. În ceea ce privește implementarea noastră folosind LitElement și presupunând că starea de activare este reprezentată de o variabilă de instanță activated
, am avea ceva de genul:
render() { if (!this.activated) return html`{this.placeholder}`; else return html` <iframe srcdoc="${this.content}" @load="${this.markLoaded}"></iframe>`; }
Comunicare inter-microaplicație
Deși microaplicațiile care formează o macroaplicație sunt prin definiție slab cuplate, ele trebuie totuși să poată comunica între ele. De exemplu, o microaplicație de navigare ar trebui să trimită o notificare că o altă microaplicație tocmai selectată de utilizator ar trebui să fie activată, iar aplicația care urmează să fie activată trebuie să primească astfel de notificări.
În conformitate cu mentalitatea noastră minimalistă, dorim să evităm introducerea multor mecanisme de transmitere a mesajelor. În schimb, în spiritul componentelor web, vom folosi evenimente DOM. Oferim un API de difuzare trivial care notifică în prealabil toate stub-urile cu privire la un eveniment iminent, așteaptă ca oricare dintre cei care au solicitat să fie activat pentru ca acel tip de eveniment să fie activat și apoi trimite evenimentul în document, pe care orice microaplicație poate asculta aceasta. Având în vedere că toate cadrele noastre iframe
sunt de aceeași origine, putem ajunge de la iframe
la pagină și viceversa pentru a găsi elemente împotriva cărora să declanșăm evenimente.
Dirijare
În zilele noastre, cu toții ne-am așteptat ca bara de adrese URL din SPA-uri să reprezinte starea de vizualizare a aplicației, astfel încât să putem tăia, lipi, trimite prin e-mail, trimite text și să le trimitem direct la o pagină din aplicație. Într-o aplicație micro-frontend, totuși, starea aplicației este de fapt o combinație de stări, câte una pentru fiecare microaplicație. Cum să reprezentăm și să controlăm acest lucru?
Soluția este să codificați starea fiecărei microaplicații într-o singură adresă URL compusă și să utilizați un mic router de macroaplicații care știe cum să pună acea adresă URL compozită împreună și să o despartă. Din păcate, acest lucru necesită o logică specifică Yumcha în fiecare microaplicație: să primească mesaje de la routerul macroaplicației și să actualizeze starea microaplicației și, dimpotrivă, să informeze ruterul macroaplicației cu privire la modificările în acea stare, astfel încât URL-ul compus să poată fi actualizat. De exemplu, s-ar putea imagina o YumchaLocationStrategy
pentru Angular sau un element <YumchaRouter>
pentru React.
Cazul Non- iframe
După cum am menționat mai sus, găzduirea microaplicațiilor în iframe
s are unele dezavantaje. Există două alternative: includeți-le direct în linie în HTML-ul paginii sau plasați-le în DOM-ul umbră. Ambele alternative reflectă oarecum avantajele și dezavantajele iframe
-urilor, dar uneori în moduri diferite.
De exemplu, politicile individuale CSP pentru microaplicații ar trebui să fie cumva îmbinate. Tehnologiile de asistență, cum ar fi cititoarele de ecran, ar trebui să funcționeze mai bine decât cu iframe
, presupunând că acceptă DOM-ul umbră (ceea ce nu toți fac încă). Ar trebui să fie simplu să aranjați înregistrarea lucrătorilor de servicii ai unei microaplicații folosind conceptul de „sfera de aplicare” a lucrătorilor de servicii, deși aplicația ar trebui să se asigure că lucrătorul său de servicii este înregistrat sub numele aplicației, nu "/"
. Niciuna dintre problemele de aspect asociate cu iframe
nu se aplică metodelor DOM inline sau umbră.
Cu toate acestea, aplicațiile construite folosind cadre precum Angular și React sunt susceptibile să fie nefericite trăind în linie sau în umbră DOM. Pentru aceștia, probabil că vom dori să folosim iframe
s.
Metodele DOM inline și shadow diferă când vine vorba de CSS. CSS va fi încapsulat curat în DOM-ul umbră. Dacă dintr-un motiv oarecare am vrut să partajăm în afara CSS cu DOM-ul umbră, ar trebui să folosim foi de stil care pot fi construite sau ceva similar. Cu microaplicațiile integrate, toate CSS-urile ar fi partajate pe întreaga pagină.
În cele din urmă, implementarea logicii pentru microaplicațiile DOM în linie și umbră în <yumcha-portal>
este simplă. Preluăm conținutul pentru o anumită microaplicație de unde a fost inserat în pagină de către logica serverului ca element HTML <template>
, îl clonăm, apoi îl anexăm la ceea ce LitElement numește renderRoot
, care este în mod normal DOM-ul umbră al elementului, dar poate de asemenea, să fie setat la elementul însuși ( this
) pentru cazul inline (non-shadow DOM).
Dar asteapta! Conținutul oferit de serverul de microaplicații este o pagină HTML întreagă. Nu putem introduce pagina HTML pentru microaplicație, cu etichete html
, head
și body
, în mijlocul celei pentru macroaplicație, nu?
Rezolvăm această problemă profitând de o particularitate a etichetei template
în care este împachetat conținutul microaplicației preluat de pe serverul de microaplicații. Se pare că atunci când browserele moderne întâlnesc o etichetă template
, deși nu o „execută”, o analizează și, în acest sens, elimină conținut nevalid, cum ar fi etichetele <html>
, <head>
și <body>
, păstrându-și în același timp conținutul interior. Deci, etichetele <script>
și <link>
din <head>
, precum și conținutul <body>
, sunt păstrate. Acesta este exact ceea ce ne dorim în scopul inserării conținutului de microaplicații în pagina noastră.
Arhitectură micro-frontend: Diavolul este în detalii
Micro frontend-urile vor prinde rădăcini în ecosistemul webapp dacă (a) se dovedesc a fi o abordare arhitecturală mai bună și (b) ne putem da seama cum să le implementăm în moduri care să satisfacă nenumăratele cerințe practice ale web-ului de astăzi.
În ceea ce privește prima întrebare, nimeni nu susține că micro frontend-urile sunt arhitectura potrivită pentru toate cazurile de utilizare. În special, ar fi puține motive pentru dezvoltarea greenfield-urilor de către o singură echipă care să adopte micro-interface-uri. Voi lăsa întrebarea despre ce tipuri de aplicații în ce tipuri de contexte ar putea beneficia cel mai mult de un model micro-frontend altor comentatori.
În ceea ce privește implementarea și fezabilitatea, am văzut că există numeroase detalii de care trebuie să ne îngrijorăm, inclusiv câteva care nici măcar nu sunt menționate în acest articol - în special autentificarea și securitatea, duplicarea codului și SEO. Cu toate acestea, sper că acest articol prezintă o abordare de bază de implementare pentru micro frontend-uri care, cu o rafinare suplimentară, poate face față cerințelor din lumea reală.
Bibliografie
- Micro Front Ends - Faceți acest stil unghiular - Partea 1
- Micro Front Ends - Faceți acest stil unghiular - Partea 2
- Evoluția unei aplicații AngularJS folosind microfrontend-uri
- Micro-Frontend-uri
- Microservicii de interfață de utilizare — inversarea modelului anti-model (micro frontend-uri)
- Microservicii de interfață de utilizare — un anti-model?
- Crearea paginilor folosind Micro-Frontend-uri are o abordare asemănătoare Yumcha a proxy-ului invers și a SSI-urilor, pe care le recomand cu căldură.
- Resurse micro-frontend
- Podium
- Nu înțeleg micro-frontend-urile. Aceasta este o prezentare generală destul de bună a tipurilor de arhitecturi micro-frontend și a cazurilor de utilizare.
- Micro-frontend-uri fără server care utilizează Vue.js, AWS Lambda și Hypernova
- Micro Frontend-uri: O prezentare generală excelentă, cuprinzătoare.