Utilizzare app-shell per rendere un'applicazione Angular più responsiva alla partenza

di Morgan Pizzini, in HTML5, Angular,

Nelle applicazioni web client-side, il caricamento dell'interfaccia è delegato all'esecuzione di codice presente nei file JavaScript, i quali inizializzano l'infrastrutta e caricano i componenti.

Questa procedura ha però un grosso svantaggio: i tempi per il primo caricamento sono elevati (il broser deve prima scaricare il bundle javascript e successivamente eseguirlo). Questo va inevitabilmente a influire negativamente sul meaningful first paint, ossia il tempo minimo in cui il browser mostra il primo componente grafico. Questo tempo è molto importante per un'applicazione web, dato che più è basso, più l'utente avrà l'impressione che il sito sia rapido e responsivo.

Fino ad ora, la modifica più comune, per ottimizzare i tempi, è stata lo spostare la sezione di pagina che deve essere visualizzata immediatamente all'interno del file index.html. In Angular, più precisamente, all'interno del tag in modo tale che al caricamento del framework, il contenuto del tag venga rimosso e sostituito dall'applicazione. In molte circostanze ci troviamo anche a scrivere il css, necessario al contenuto da mostrare all'avvio, inline nella pagina, il tutto per evitare i tempi di download di eventuali stylesheet.

Questa pratica, oltre a fare uso di un comportamento non formalizzato del framework, porta nella maggior parte dei casi a scrivere codice duplicato, per creare ciò che si definisce lo skeleton della pagina cioè la visualizzazione di uno spazio in cui andrà a inserirsi il menù, colori di background in linea con lo stile dell'applicazione e un'animazione di loading.

Attraverso l'app-shell possiamo invece utilizzare la fase di building per convertire il codice HTML/TS/CSS di un componente in codice inline inserto all'interno della pagina index.html. Ciò permetterà all'applicazione di ridurre i tempi per la visualizzazione del primo componente, ma, cosa più importante, consentirà a noi sviluppatori di scrivere codice restando all'interno del framework, e avere quindi accesso a tutte le sue potenzialità, senza la necessità di duplicare codice.

Per poter creare il progetto app-shell abbiamo bisogno di basarci su Angular Universal, il quale, nella sua versione più completa, fornisce un server express.js in grado di eseguire il pre-render della pagina a seguito di una richiesta. Nel nostro caso utilizzeremo invece la sua seconda funzionalità: eseguire il pre-render di un componente direttamente nella fase di build.

Dopo aver creato un'applicazione Angular, procediamo a creare, tramite CLI, l'infrastruttura necessaria affinchè i processi di build possano creare l'app-shell

ng new my-app --routing
cd my-app
ng generate app-shell ?client-project my-app -universal-project my-app-server

  • -client-project: il progetto su cui inizializzare l'app-shell
  • -universal-project: l'identificativo del progetto server

Il comando ng generate esegue delle modifiche al codice esistente e aggiunge nuovi file. Illustriamo prima le modifiche ai file esistenti:

  • package.json: aggiunta la dipendenza a @angular/platform-server, la quale contiente tutto il codice necessario per l'app-shell.
  • main.ts: le operazioni di bootstrap dell'applicazione vengono da ora subordinate all'emissione dell'evento DOMContentLoaded da parte del document.
  • app.module.ts: il BrowserModule viene importato con specifici provider forniti dal metodo withServerTransition({ appId: 'myAppId' }).
  • angular.json: aggiunta di due nuove opzioni di build: server e app-shell che verrano utilizzate nel momento in cui chiederemo al sistema di eseguire una build incorporando l'app-shell.

Nel progetto, oltre alle modifiche ai file responsabili del caricamento, ne troveremo anche dei nuovi che andranno a interagire con la procedura di build.

  • tsconfig.server.json: contiene le opzioni di compilazione usate dall'architettura server dell' angular.json.
  • main.server.ts: entry-point dell'app-shell che esporta il modulo AppServerModule.
  • app.server.module.ts: modulo contente le referenze verso il/i componente/i da incorporare nel file index.html e una route con un nome fittizio, che non ritroveremo poi tra le nostre route, ma verrà utilizzato dalla CLI durante la build per identificare il primo componente da cui partire.
  • app-shell: componente che verrà trascritto nel file index.html.

Build e differenze


Per poter buildare l'applicazione bisognerà utilizzare l'architettura inserita in angular.json utilizzando il comando

ng run my-app:app-shell

// produzione
ng run my-app:app-shell:production

Al termine delle operazioni troveremo nel file dist/my-app/index.html il componente app-shell compilato ed inserito inline nell'HTML.

Per verificare l'effettiva riduzione dei tempi di meaningful first paint si potrà utilizzare il comando http-server -c-1 per la creazione di un reverse proxy, fornito dal pacchetto npm http-server, eseguito all'interno della directory compilata. Navigando poi all'url ed utilizzando il tab performance dei dev-tools del browser, possiamo registrare le procedure eseguite per il caricamento della pagina. Al colore viola corrisponde l'azione di rendering, e nel momento in cui apparirà all'interno del grafico sapremo che la pagina web starà presentando del contenuto.

Per controprova, per verificarne l'efficienza possiamo buildare l'applicazione con il comando ng build (se l'app-shell è stata buildata in produzione, per avere un paragone coerente, occorrerà aggiungere l'opzione --prod), riutilizzare http-server e registrare nuovamente le prestazioni utilizzando il tab performance. Noteremo due cose: la prima è un processo di rendering eseguito pochi decimi di secondo dopo l'esecuzione della richiesta, ma non dobbiamo farci trarre in inganno dato che è un rendering che visualizzerà solo una pagina bianca, coerentemente con il contenuto di index.html, il secondo invece è quello che disegnerà la nostra interfaccia nel browser, a seguito dell'esecuzione del JavaScript. Numeri alla mano, la visualizzazione di una nuova applicazione, creata tramite CLI, avverrà circa quattro decimi più tardi rispetto all'app-shell.

Sebbene i pochi decimi guadagnati per il rendering di un'applicazione senza contenuto, il vantaggio nell'utilizzo dell'app-shell si noterà quando l'applicazione conterrà vari moduli, componenti e servizi che dovranno essere caricati all'avvio, e che quindi andranno inevitabilmente a ritardare i tempi per il primo caricamento. Il processo di creazione, e le modifiche effettuate per l'app-shell, come abbiamo visto, non sono invasive per il progetto, e quindi possono essere facilmente abilitate anche su applicazioni già esistenti, senza la necessità di modificarne la struttura.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi