Tecniche, strategie e ricette per sviluppare un’applicazione web moderna con il contributo di team diversi che possano rilasciare funzionalità in maniera indipendente.
Cosa sono i Micro Frontend?
Il termine Micro Frontend è apparso per la prima volta su ThoughtWorks Technology Radar alla fine del 2016. Estende i concetti dei microservizi al mondo del frontend. Il trend corrente era di costruire applicazioni browser potenti e ricche di funzionalità - note come single page application - in cima ad architetture a microservizi. Con il tempo, questo strato di frontend, sviluppato il più delle volte da un team a sé stante, cresce e diventa difficile da manutenere. Lo chiamiamo Frontend Monolitico.
L’idea alla base dei Micro Frontend è - invece - di pensare al sito web o alla web app come a una composizione di funzionalità che fanno capo a team indipendenti. Ogni team ha una sua area di business, o missione, diversa, di cui si prende cura e in cui si specializza. Ogni team è cross funzionale e sviluppa le sue funzionalità end-to-end, dal database all’interfaccia utente.
C’è da dire che quest’idea non è nuova. Ha molti punti in comune con il concetto di Sistemi auto-contenuti. In passato, approcci simili venivano chiamati Integrazione del Frontend per Sistemi Verticalizzati. Ma, chiaramente, Micro Frontend è un termine più comodo e meno altisonante.
Frontend Monolitici
Organizzazione in verticali
Cos’è un’applicazione web moderna?
Nell’introduzione, ho usato l’espressione “costruire un’applicazione web moderna”. Definiamo le assunzioni collegate a questa definizione.
Partiamo da lontano: Aral Balkan ha scritto un articolo su quello che chiama il Continuum documenti-applicazioni. Ha proposto l’immagine di una bilancia scorrevole alla cui sinistra c’è un sito costruito da documenti statici, connessi attraverso link, mentre alla destra c’è un’applicazione senza contenuti, guidata puramente da comportamenti (behaviour driven), come un editor di foto.
Se il tuo progetto si posiziona alla sinistra dello spettro, è adatto a un’integrazione a livello di webserver. In tale modello, un server raccoglie e concatena stringhe HTML provenienti da tutti i componenti che costituiscono la pagina richiesta dall’utente. Gli aggiornamenti sono fatti ricaricando la pagina dal server o sostituendone alcune parti con Ajax. Gustaf Nilsson Kotte ha scritto un articolo esaustivo su quest’argomento.
Quando la tua interfaccia utente deve mostrare un feedback immediato, anche in caso di cattiva connessione, non basta più un sito costruito interamente sul server. Per implementare tecniche come UI ottimistica o Skeleton Screens devi poter aggiornare la UI sul device stesso. La definizione di Google Progressive Web Apps descrive convincentemente l’atto di bilanciamento che consiste nell’essere un bravo cittadino del web (enhancement progressivo), garantendo allo stesso tempo performance simili a quelle di un’app. Questo tipo d’applicazione si pone più o meno a metà del continuum sito-app. Qui non basta più una soluzione basata solo sul server. Dobbiamo spostare l’integrazione nel browser, e questo è il focus di quest’articolo.
Idee fondamentali alla base dei Micro Frontend
- Sii Agnostico sulla Tecnologia
Ogni team dovrebbe poter scegliere e aggiornare il suo stack senza doversi coordinare con gli altri team. Gli Elementi Custom sono un modo ottimo per nascondere i dettagli implementativi, fornendo al contempo un’interfaccia neutrale agli altri. - Isola il Codice del Team
Non condividere il runtime, anche se tutti i team usano lo stesso framework. Costruisci applicazioni indipendenti e auto-contenute. Non fare affidamento sullo stato condiviso o su variabili globali. - Stabilisci Prefissi per i Team
Condividi una naming convention laddove non sia ancora possibile l’isolamento. Fornisci un namespace a CSS, Eventi, Local Storage e Cookies per evitare collisioni e per chiarire chi è l’owner. - Privilegia le Funzionalità Native del Browser rispetto alle API Custom Usa Gli Eventi del Browser per la comunicazione invece di implementare un sistema globale PubSub. Se proprio devi creare un’API cross-team, cerca di tenerla la più semplice possibile.
- Costruisci un Sito Resiliente
Le feature del sito dovrebbero rimanere utili anche se JavaScript fallisce o non è ancora stato eseguito. Usa il Rendering Universale e l’Enhancement Progressivo per migliorare le performance percepite.
Il DOM è l’API
Gli Elementi Custom, che rappresentano l’aspetto d’interoperabilità della specifica Web Components, sono una buona primitiva per realizzare un’integrazione nel browser. Ogni team costruisce il suo componente usando la tecnologia che preferisce e la avvolge in un Elemento Custom (esempio: <order-minicart></order-minicart>
). La specifica DOM di questo particolare elemento (tag-name, attributi ed eventi) fa da contratto o API pubblica per gli altri team. Il vantaggio è che questi ultimi possono usare il componente e le sue funzionalità senza conoscerne l’implementazione: devono solo interagire col DOM.
Ma gli Elementi Custom, da soli, non sono la soluzione a tutti i nostri problemi. Per indirizzare l’enhancement progressivo, il rendering universale e il routing, abbiamo bisogno di software aggiuntivo.
Questa pagina è divisa in due aree principali. Prima dobbiamo discutere della Composizione della Pagina - ovvero come assemblare una pagina da più componenti gestiti da team diversi. Dopo, mostreremo esempi per implementare le Transizioni di Pagina lato client.
Composizione della Pagina
Oltre proprio all’integrazione del codice lato client e server scritto con framework diversi, ci sono un sacco di argomenti a lato da discutere: i meccanismi per isolare il JavaScript, evitare i conflitti CSS, caricare le risorse quando serve, condividere le risorse comuni fra i team, gestire la richiesta di dati dal server e pensare a una giusta gestione degli stati di caricamento per l’utente. Affronteremo questi argomenti un passo alla volta.
Il Prototipo Base
Useremo come base per gli esempi seguenti la pagina prodotto di un negozio di modellini di trattore.
Espone un selettore di varianti per scegliere fra i tre diversi modellini di trattore. A ogni cambiamento, si aggiornano l’immagine, il nome, il prezzo e le raccomandazioni del prodotto. C’è anche un pulsante d’acquisto, che aggiunge la variante selezionata al cestino, e un mini carrello alla sommità della pagina, che si aggiorna di conseguenza.
provalo nel browser & ispeziona il codice
Tutto l’HTML è generato usando JavaScript puro e stringhe template ES6 senza nessuna dipendenza. Il codice usa una semplice separazione stato/markup e ri-renderizza tutto l’HTML lato client a ogni cambiamento - non c’è nessun DOM diffing strano e nessun rendering universale per ora. Inoltre, non c’è separazione fra team - il codice è scritto in un file js/css.
Integrazione lato Client
In quest’esempio, la pagina è divisa in componenti/frammenti separati, gestiti da tre team. Team Checkout (blu) adesso è responsabile di tutto quello che riguarda il processo d’acquisto - nello specifico, il pulsante d’acquisto e il mini carrello. Il Team Inspire (verde) gestisce i prodotti raccomandati su questa pagina. La pagina stessa è gestita dal Team Product (rosso).
prova nel browser & ispeziona il codice
Ogni Team
Il Team Product decide che funzionalità dev’essere inclusa e dove deve essere posizionata nel layout. La pagina contiene informazioni che possono essere fornite dallo stesso Team Product, come il nome del prodotto, l’immagine e le varianti disponibili. La pagina include anche frammenti (elementi custom) dagli altri team.
Come creare un Elemento Custom?
Prendiamo per esempio il pulsante d’acquisto. Il Team Product include il pulsante aggiungendo semplicemente <blue-buy sku="t_porsche"></blue-buy>
alla posizione desiderata del markup. Per farlo funzionare, il Team Checkout deve registrare l’elemento blue-buy
sulla pagina.
class BlueBuy extends HTMLElement {
connectedCallback() {
this.innerHTML = `<button type="button">acquista a 66,00 €</button>`;
}
disconnectedCallback() { ... }
}
window.customElements.define('blue-buy', BlueBuy);
Adesso, ogni volta che il browser trova un nuovo tag blue-buy
, viene chiamata la callback connectedCallback
. this
è un riferimento al nodo DOM root dell’elemento custom. Si possono usare tutte le proprietà e i metodi di un elemento DOM standard, come innerHTML
or getAttribute()
.
Quando dai un nome all’elemento, l’unico requisito definito dalla specifica è che il nome deve includere un trattino (-) per mantenere la compatibilità con tag HTML futuri. Nei prossimi esempi, useremo la convenzione [colore_del_team]-[feature]
. Il namespace del team ci protegge da collisioni e, in aggiunta, diventa in questo modo ovvio chi detiene una feature, guardando semplicemente il DOM.
Comunicazione Padre-Figlio / Modifica del DOM
Se l’utente seleziona un altro trattore nel selettore di varianti, dev’essere aggiornato corrispondentemente il pulsante d’acquisto. A questo scopo, il Team Prodotto può semplicemente togliere l’elemento esistente dal DOM e inserirne uno nuovo.
container.innerHTML;
// => <blue-buy sku="t_porsche">...</blue-buy>
container.innerHTML = '<blue-buy sku="t_fendt"></blue-buy>';
La callback disconnectedCallback
del vecchio elemento viene invocata in maniera sincrona per dare all’elemento la possibilità di fare pulizia di cose come i listener di eventi. Dopo, viene invocata la callback connectedCallback
dell’elemento appena creato t_fendt
.
Un’altra possibilità più performante è di aggiornare solo l’attributo sku
dell’elemento esistente:
document.querySelector('blue-buy').setAttribute('sku', 't_fendt');
Se il Team Product usasse un motore di template che implementa il DOM diffing, come React, questo verrebbe eseguito automaticamente dall’algoritmo.
Per supportare questo comportamento, l’Elemento Custom può implementare la callback attributeChangedCallback
e specificare una lista di observedAttributes
per cui dovrebbe essere scatenata questa callback.
const prices = {
t_porsche: '66,00 €',
t_fendt: '54,00 €',
t_eicher: '58,00 €',
};
class BlueBuy extends HTMLElement {
static get observedAttributes() {
return ['sku'];
}
connectedCallback() {
this.render();
}
render() {
const sku = this.getAttribute('sku');
const price = prices[sku];
this.innerHTML = `<button type="button">acquista a ${price}</button>`;
}
attributeChangedCallback(attr, oldValue, newValue) {
this.render();
}
disconnectedCallback() {...}
}
window.customElements.define('blue-buy', BlueBuy);
Per evitare duplicazioni, introduciamo un metodo render()
che viene chiamato da connectedCallback
e attributeChangedCallback
. Questo metodo raccoglie i dati necessari e il nuovo markup va in innerHTML. Se si decide di usare un motore o framework di template più sofisticato nell’Elemento Custom, questo è il posto dove dovrebbe andare il suo codice d’inizializzazione.
Supporto dei Browser
L’esempio precedente usa la specifica versione 1 degli Elementi Custom, che al momento è supportata da Chrome, Safari e Opera. Però, con il progetto document-register-element è stato reso disponibile un polyfill rodato e leggero per far funzionare tutto ciò in tutti i browser. Sotto il cofano, usa un’API ampiamente supportata, la Mutation Observer, dunque non ci sono controlli strani in background dell’albero DOM.
Compatibilità Framework
Siccome gli Elementi Custom sono uno standard web, li supportano tutti i principali framework JavaScript, come Angular, React, Preact, Vue o Hyperapp. Però, quando entri nei dettagli, alcuni di questi framework hanno ancora qualche problemino implementativo. Su Custom Elements Everywhere, Rob Dodson ha messo in piedi una suite di test che evidenzia i problemi irrisolti.
Evitiamo l’Anarchia dei Framework
Usare gli Elementi Custom è un ottimo modo per raggiungere un alto grado di disaccoppiamento fra i frammenti dei diversi team. In questo modo, ogni team è libero di scegliere un framework di frontend. Però, solo perché puoi farlo non significa che sia saggio mixare tecnologie differenti. Proviamo ad evitare l’Anarchia dei Micro Frontend e a creare invece un livello di allineamento ragionevole fra i vari team. Così, i team possono scambiarsi insegnamenti e best practice. Ci renderà poi la vita più facile se vogliamo stabilire una pattern library centralizzata. Detto ciò, la possibilità di mixare le tecnologie può essere utile quando lavori con un’applicazione legacy e vuoi migrarla a uno stack tecnologico nuovo.
Comunicazione fra Figlio-Genitore o fra Fratelli / Eventi DOM
Ma passare gli attributi non è sufficiente per tutte le interazioni. Nel nostro esempio, il mini carrello dovrebbe aggiornarsi quando l’utente clicca sul pulsante d’acquisto.
Entrambi i frammenti sono di proprietà del Team Checkout (blu), quindi loro potrebbero creare una qualche API JavaScript interna che permettesse al mini carrello di sapere quando è stato premuto un pulsante. Ma questo significherebbe che le istanze dei componenti dovrebbero conoscersi a vicenda e questa sarebbe pure una violazione dell’isolamento.
Un modo più pulito è di usare un meccanismo PubSub, in cui un componente può pubblicare un messaggio e altri componenti possono sottoscriversi a certi topic. Per fortuna, i browser hanno nativamente questa funzionalità. Questo è esattamente come funzionano eventi del browser tipo click
, select
o mouseover
. In aggiunta agli eventi nativi, c’è anche la possibilità di creare eventi di livello superiore con new CustomEvent(...)
. Gli eventi sono sempre legati al nodo DOM su cui sono stati creati o dispacciati. La maggior parte degli eventi nativi supporta anche il bubbling. Questo rende possibile ascoltare tutti gli eventi su un sotto-albero specifico del DOM. Se vuoi ascoltare tutti gli eventi della pagina, attacca l’event listener all’elemento window. Ecco come appare la creazione dell’evento blue:basket:changed
nell’esempio:
class BlueBuy extends HTMLElement {
[...]
connectedCallback() {
[...]
this.render();
this.firstChild.addEventListener('click', this.addToCart);
}
addToCart() {
// magari qui chiama un'API
this.dispatchEvent(new CustomEvent('blue:basket:changed', {
bubbles: true,
}));
}
render() {
this.innerHTML = `<button type="button">acquista</button>`;
}
disconnectedCallback() {
this.firstChild.removeEventListener('click', this.addToCart);
}
}
Il mini carrello può adesso sottoscriversi a quest’evento su window
ed essere avvisato quando dovrebbe aggiornare i suoi dati.
class BlueBasket extends HTMLElement {
connectedCallback() {
[...]
window.addEventListener('blue:basket:changed', this.refresh);
}
refresh() {
// leggi dati nuovi e renderizzali
}
disconnectedCallback() {
window.removeEventListener('blue:basket:changed', this.refresh);
}
}
Con quest’approccio, il frammento del mini carrello aggiunge un listener a un elemento del DOM che è fuori dal suo scope (window
). Questo dovrebbe essere OK in molte applicazioni ma, se proprio non piace, si può anche implementare un approccio in cui la pagina stessa (Team Product) ascolta un evento e notifica il mini carrello chiamando refresh()
sull’elemento del DOM.
// page.js
const $ = document.getElementsByTagName;
$('blue-buy')[0].addEventListener('blue:basket:changed', function() {
$('blue-basket')[0].refresh();
});
Non è comune chiamare imperativamente metodi del DOM, ma si può trovare un esempio nella video element api. Se possibile, dovrebbe essere preferito l’uso dell’approccio dichiarativo (cambio dell’attributo).
Rendering lato Server / Rendering Universale
Gli Elementi Custom vanno benissimo per integrare componenti nel browser. Però, quando costruisci un sito che è accessibile dal web, è probabile che siano importanti pure le performance di caricamento iniziale e gli utenti vedranno lo schermo bianco finché non vengono scaricati ed eseguiti tutti i framework JavaScript. In aggiunta, è utile capire cosa succede quando il JavaScript fallisce o è bloccato. Jeremy Keith ne spiega l’importanza nel suo eBook / podcast Resilient Web Design. Dunque, la capacità di renderizzare i contenuti core sul server è chiave. Purtroppo, la specifica sui web component non parla proprio di rendering lato server. Niente JavaScript, niente Elementi Custom :(
Elementi Custom + Server Side Includes = ❤️
Per far funzionare il rendering lato server, bisogna fare refactoring dell’esempio precedente. Ogni team ha il proprio server Express ed è accessible via web anche il metodo render()
dell’Elemento Custom.
$ curl http://127.0.0.1:3000/blue-buy?sku=t_porsche
<button type="button">acquista a 66,00 €</button>
Il nome del tag dell’Elemento Custom viene usato come nome del percorso - gli attributi diventano query parameters. Adesso abbiamo un modo per renderizzare lato server il contenuto di ogni componente. Insieme agli Elementi Custom <blue-buy>
, si ottiene qualcosa di molto simile a un Componente Web Universale:
<blue-buy sku="t_porsche">
<!--#include virtual="/blue-buy?sku=t_porsche" -->
</blue-buy>
Il commento #include
fa parte dei Server Side Includes, che è una funzionalità disponibile nella maggior parte dei server web. Sì, è la stessa tecnologia che si usava una volta per inglobare la data corrente sui siti web. Ci sono anche tecniche alternative come ESI, nodesi, compoxure e tailor ma, per i nostri progetti, le SSI si sono mostrate una soluzione semplice e incredibilmente stabile.
Il commento #include
viene sostituito dalla risposta di /blue-buy?sku=t_porsche
prima che il server invii la pagina completa al browser. La configurazione in nginx appare così:
upstream team_blue {
server team_blue:3001;
}
upstream team_green {
server team_green:3002;
}
upstream team_red {
server team_red:3003;
}
server {
listen 3000;
ssi on;
location /blue {
proxy_pass http://team_blue;
}
location /green {
proxy_pass http://team_green;
}
location /red {
proxy_pass http://team_red;
}
location / {
proxy_pass http://team_red;
}
}
La direttiva ssi: on;
abilita la funzionalità SSI. Vengono aggiunti un blocco upstream
e uno location
per team, per assicurarsi che tutti gli URL che cominciano con /blue
siano diretti all’applicazione giusta (team_blue:3001
). In aggiunta, la rotta /
viene mappata al Team Red, che controlla la homepage / pagina prodotto.
Quest’animazione mostra il negozio di modellini di trattori in un browser che ha JavaScript disabilitato.
I pulsanti di selezione della variante adesso sono proprio link e ogni click porta a ricaricare la pagina. La linea di comando sulla destra mostra il processo con cui una richiesta della pagina viene inoltrata al Team Rosso, che controlla la pagina prodotto; dopo, il markup viene fornito dai frammenti dei Team Blu e Verde.
Se viene riattivato JavaScript, sarà visibile solo il messaggio di log per la prima richiesta. Tutte le modifiche seguenti al trattore saranno gestite lato client, come nel primo esempio. In un esempio successivo, i dati dei prodotti saranno estratti dal JavaScript e caricati da una API REST per quel che serve.
Puoi giocare con quest’esempio sulla tua macchina locale. Devi installare solo Docker Compose.
git clone https://github.com/neuland/micro-frontends.git
cd micro-frontends/2-composition-universal
docker-compose up --build
Docker fa partire nginx sulla porta 3000 e costruisce un’immagine node.js per ogni team. Quando apri http://127.0.0.1:3000/ nel browser, dovresti vedere un trattore rosso. I log combinati di docker-compose
permettono di vedere facilmente cosa succede sulla rete. Purtroppo non c’è modo di controllare il colore dell’output, quindi devi rassegnarti al fatto che il Team Blu potrebbe essere evidenziato in verde :)
I file src
sono mappati nei container individuali e l’applicazione node ripartirà quando fai una modifica al codice. Cambiare il file nginx.conf
richiede un riavvio di docker-compose
per avere effetto. Sentiti libero di giochicchiare e di fornire un feedback.
Lettura dei dati & stati di Caricamento
Uno svantaggio dell’approccio SSI/ESI è che il frammento più lento determina il tempo di risposta dell’intera pagina.
Quindi, può far bene cachare la risposta di un frammento.
Per frammenti che sono dispendiosi da produrre e difficili da mettere in cache, è spesso indicato escluderli dal rendering iniziale.
Possono essere caricati in maniera asincrona nel browser.
Nel nostro esempio, un buon candidato per questo è il frammento green-recos
, che mostra raccomandazioni personalizzate.
Una possibile soluzione sarebbe che il Team Rosso saltasse proprio l’SSI Include.
Prima
<green-recos sku="t_porsche">
<!--#include virtual="/green-recos?sku=t_porsche" -->
</green-recos>
Dopo
<green-recos sku="t_porsche"></green-recos>
Nota a lato importante. Gli Elementi Custom non possono essere self-closing, quindi è sbagliato scrivere <green-recos sku="t_porsche" />
Il rendering avviene solo nel browser. Ma, come si può vedere nell’animazione, questo cambio ha introdotto un reflow sostanziale della pagina. Il principio, la sezione raccomandazioni è bianca. Il JavaScript del Team Verde viene caricato ed eseguito. Viene fatta la chiamata API per ricevere le raccomandazioni personalizzate. Viene renderizzato il markup delle raccomandazioni e vengono richieste le immagini associate. Il frammento ora ha bisogno di più spazio e spinge giù il layout della pagina.
Ci sono diverse opzioni per evitare un riposizionamento fastidioso come questo.
Il Team Rosso, che controlla la pagina, potrebbe rendere fissa l’altezza del container delle raccomandazioni. Su un sito responsive è spesso ingannevole determinare l’altezza, perché potrebbe differire per schermi diversi. Ma il problema più serio è che questo tipo di accordi inter-team crea un accoppiamento stretto fra i Team Rosso e Verde. Se il Team Verde vuole introdurre una sotto-intestazione aggiuntiva nell’elemento raccomandazioni, dovrebbe coordinarsi con il Team Rosso per la nuova altezza. I team dovrebbero rilasciare simultaneamente per evitare che si rompa il layout.
Un metodo migliore è di usare una tecnica chiamata Skeleton Screens.
Il Team Rosso lascia l’include SSI green-recos
nel markup.
In più, il Team Verde cambia il metodo di renderizzazione lato-server del suo frammento in modo che produca una versione schematica del contenuto.
Il markup skeleton_ può riusare parte degli stili del layout del contenuto reale.
Così prenota lo spazio necessario e il riempimento del contenuto reale non comporta un salto.
Gli Skeleton screen sono anche molto utili per il rendering lato client. Quando il tuo Elemento Custom viene inserito nel DOM per un’azione dell’utente, potrebbe renderizzare immediatamente lo scheletro finché non arrivano i dati di cui ha bisogno dal server.
Anche per un cambio d’attributo (per esempio per la selezione di una variante) si può decidere di passare alla vista scheletro finché non arrivano i nuovi dati.
In questo modo, l’utente riceve un’indicazione che qualcosa sta succedendo nel frammento. Ma quando l’endpoint risponde velocemente, può dare fastidio anche un piccolo sfarfallio dello scheletro fra i dati vecchi e nuovi. Può aiutare preservare i vecchi dati o usare timeout intelligenti. Quindi, usa questa tecnica saggiamente e cerca di ottenere il feedback degli utenti.
Navigare Fra le Pagine
continua presto … (Giuro)
Guarda il Repo Github per ricevere le notifiche
Risorse Aggiuntive
- Libro: Micro Frontends in Action. Scritto da me.
- Discussione: Micro Frontends - MicroCPH, Copenhagen 2019 (Slides) I dettagli più essenziali di Frontend, Backend, 🌈 Happyend
- Discussione: Micro Frontends - Web Rebels, Oslo 2018 (Slides) Pensa in piccolo, Evita il Monolite,❤️ il Backend
- Slides: Micro Frontends - JSUnconf.eu 2017
- Discussione: Break Up With Your Frontend Monolith - JS Kongress 2017 Elisabeth Engel parla dell’implementazione dei Micro Frontends in gutefrage.net
- Articolo: Micro Frontends Articolo di Cam Jackson sul blog di Martin Fowler
- Post: Micro frontends - a microservice approach to front-end web development Tom Söderlund spiega i concetti base e fornisce link sull’argomento
- Post: Microservices to Micro-Frontends Sandeep Jain riassume i principi chiave di microservizi e micro frontends
- Link Collection: Micro Frontends by Elisabeth Engel una lista esaustiva di post, discorsi, strumenti e altre risorse sull’argomento
- Awesome Micro Frontends una lista curata di link di Christian Ulbrich 🕶
- Custom Elements Everywhere Per assicurarsi che i framework e gli elementi custom siano BFF (best friend forever)
- I trattori si possono comprare da manufactum.com :)
Questo negozio è stato sviluppato da due team usando le tecniche qui descritte.
Tecniche Correlate
- Post: Cookie Cutter Scaling David Hammet ha scritto una serie di blog post su quest’argomento
- Wikipedia: Java Portlet Specification Specifica che si rivolge ad argomenti simili per la costruzione di portali enterprise.
Cose in Arrivo … (molto presto)
- Use Cases
- Navigazione fra le pagine
- navigazione soft vs. hard
- universal router
- …
- Navigazione fra le pagine
- Argomenti a lato
- CSS Isolato / Interfaccia Utente Coerente / Style Guide & Pattern Library
- Performance al caricamento iniziale
- Performance mentre usi il sito
- Caricare il CSS
- Caricare JS
- Test d’Integrazione
- …
Hanno contribuito
- Koike Takayuki che ha tradotto il sito in Giapponese.
- Jorge Beltrán che ha tradotto il sito in Spagnolo.
- Bruno Carneiro che ha tradotto il sito in Portoghese.
- Soobin Bak che ha tradotto il sito in Coreano.
- Sergei Babin che ha tradotto il sito in Russo.
- Shiwei Yang che ha tradotto il sito in Cinese.
- Riccardo Moschetti che ha tradotto il sito in Italiano.
Questo sito è generato da Github Pages. Il codice si trova qui: neuland/micro-frontends.