Questo codelab esplora come la minimizzazione e la compressione del bundle JavaScript per la seguente applicazione migliorano il rendimento della pagina riducendo le dimensioni della richiesta dell'app.
Misura
Prima di aggiungere ottimizzazioni, è sempre consigliabile analizzare lo stato attuale dell'applicazione.
- Per visualizzare l'anteprima del sito, premi Visualizza app. Quindi premi
Schermo intero
.
Questa app, trattata anche nel codelab "Rimuovi codice inutilizzato", ti consente di votare il tuo gattino preferito. 🐈
Ora dai un'occhiata alle dimensioni di questa applicazione:
- Premi "Control+Maiusc+J" (o "Command+Opzione+J" su Mac) per aprire DevTools.
- Fai clic sulla scheda Rete.
- Seleziona la casella di controllo Disattiva cache.
- Ricarica l'app.
Sebbene siano stati fatti molti progressi nel codelab "Remove unused code" per ridurre le dimensioni del bundle, 225 KB sono ancora piuttosto grandi.
Minimizzazione
Considera il seguente blocco di codice.
function soNice() {
let counter = 0;
while (counter < 100) {
console.log('nice');
counter++;
}
}
Se questa funzione viene salvata in un file separato, le dimensioni del file sono circa 112 B (byte).
Se tutti gli spazi vuoti vengono rimossi, il codice risultante ha il seguente aspetto:
function soNice(){let counter=0;while(counter<100){console.log("nice");counter++;}}
Le dimensioni del file saranno ora di circa 83 B. Se viene ulteriormente manipolato riducendo la lunghezza del nome della variabile e modificando alcune espressioni, il codice finale potrebbe avere questo aspetto:
function soNice(){for(let i=0;i<100;)console.log("nice"),i++}
Le dimensioni del file ora raggiungono 62 B.
A ogni passaggio, il codice diventa più difficile da leggere. Tuttavia, il motore JavaScript del browser interpreta ciascuno di questi elementi esattamente nello stesso modo. Il vantaggio di offuscare il codice in questo modo può contribuire a ridurre le dimensioni dei file. 112 B non erano molti all'inizio, ma c'è stata comunque una riduzione delle dimensioni del 50%.
In questa applicazione, webpack versione 4 viene utilizzato come
bundler di moduli. La versione specifica è visibile in package.json
.
"devDependencies": {
//...
"webpack": "^4.16.4",
//...
}
La versione 4 comprime già il bundle per impostazione predefinita durante la modalità di produzione. Utilizza
TerserWebpackPlugin
un plug-in per Terser.
Terser è uno strumento popolare utilizzato per comprimere il codice JavaScript.
Per farti un'idea di come appare il codice compresso, fai clic su
main.bundle.js
mentre ti trovi ancora nel riquadro Rete di DevTools. Ora fai clic sulla scheda
Risposta.
Il codice nella sua forma finale, compresso e modificato, viene mostrato nel corpo della risposta.
Per scoprire le dimensioni del bundle se non fosse stato compresso, apri
webpack.config.js
e aggiorna la configurazione di mode
.
module.exports = {
mode: 'production',
mode: 'none',
//...
Ricarica l'applicazione e controlla di nuovo le dimensioni del bundle tramite il riquadro Rete di DevTools.
Una differenza piuttosto significativa. 😅
Assicurati di annullare le modifiche qui prima di continuare.
module.exports = {
mode: 'production',
mode: 'none',
//...
L'inclusione di una procedura per comprimere il codice nell'applicazione dipende dagli strumenti che utilizzi:
- Se viene utilizzata webpack v4 o versioni successive, non è necessario alcun intervento aggiuntivo in quanto il codice viene minimizzato per impostazione predefinita in modalità di produzione. 👍
- Se viene utilizzata una versione precedente di webpack, installa e includi
TerserWebpackPlugin
nel processo di build di webpack. La documentazione spiega questo aspetto nel dettaglio. - Esistono anche altri plug-in di minimizzazione che possono essere utilizzati al loro posto, come BabelMinifyWebpackPlugin e ClosureCompilerPlugin.
- Se non viene utilizzato alcun bundler di moduli, utilizza Terser come strumento CLI o includilo direttamente come dipendenza.
Compressione
Sebbene il termine "compressione" venga a volte utilizzato in modo generico per spiegare come il codice viene ridotto durante il processo di minimizzazione, in realtà non viene compresso in senso letterale.
Il termine compressione in genere si riferisce al codice modificato utilizzando un algoritmo di compressione dei dati. A differenza della minificazione, che fornisce codice perfettamente valido, il codice compresso deve essere decompresso prima di essere utilizzato.
Con ogni richiesta e risposta HTTP, i browser e i server web possono aggiungere
intestazioni per includere
informazioni aggiuntive sull'asset recuperato o ricevuto. Puoi visualizzarlo nella scheda Headers
del riquadro Rete di DevTools, dove vengono mostrati tre tipi:
- Generale rappresenta le intestazioni generali pertinenti all'intera interazione richiesta-risposta.
- Intestazioni di risposta mostra un elenco di intestazioni specifiche della risposta effettiva del server.
- Intestazioni richieste mostra un elenco di intestazioni allegate alla richiesta dal client.
Dai un'occhiata all'intestazione accept-encoding
in Request Headers
.
accept-encoding
viene utilizzato dal browser per specificare i formati di codifica dei contenuti o gli algoritmi di compressione supportati. Esistono molti
algoritmi di compressione del testo, ma solo tre sono
supportati qui per la compressione (e la decompressione) delle richieste di rete HTTP:
- Gzip (
gzip
): il formato di compressione più utilizzato per le interazioni tra server e client. Si basa sull'algoritmo Deflate ed è supportato in tutti i browser attuali. - Deflate (
deflate
): non utilizzato di frequente. - Brotli (
br
): un algoritmo di compressione più recente che mira a migliorare ulteriormente i rapporti di compressione, il che può comportare caricamenti delle pagine ancora più veloci. È supportato nelle versioni più recenti della maggior parte dei browser.
L'applicazione di esempio in questo tutorial è identica a quella completata nel codelab "Remove unused code", tranne per il fatto che Express viene ora utilizzato come framework del server. Nelle sezioni successive vengono esaminate la compressione statica e quella dinamica.
Compressione dinamica
La compressione dinamica prevede la compressione degli asset al volo quando vengono richiesti dal browser.
Pro
- La creazione e l'aggiornamento delle versioni compresse salvate degli asset non devono essere eseguiti.
- La compressione al volo funziona particolarmente bene per le pagine web generate dinamicamente.
Contro
- La compressione dei file a livelli più alti per ottenere rapporti di compressione migliori richiede più tempo. Ciò può causare un calo delle prestazioni mentre l'utente attende la compressione degli asset prima che vengano inviati dal server.
Compressione dinamica con Node/Express
Il file server.js
è responsabile della configurazione del server Node che ospita l'applicazione.
const express = require('express');
const app = express();
app.use(express.static('public'));
const listener = app.listen(process.env.PORT, function() {
console.log('Your app is listening on port ' + listener.address().port);
});
Al momento, questa operazione importa express
e utilizza il middleware express.static
per caricare tutti i file HTML, JS e CSS statici nella
directory public/
(e questi file vengono creati da webpack a ogni build).
Per assicurarti che tutte le risorse vengano compresse ogni volta che vengono richieste, puoi utilizzare la libreria middleware di
compressione. Inizia aggiungendolo come devDependency
in package.json
:
"devDependencies": {
//...
"compression": "^1.7.3"
},
e importalo nel file del server, server.js
:
const express = require('express');
const compression = require('compression');
e aggiungilo come middleware prima che express.static
venga montato:
//...
const app = express();
app.use(compression());
app.use(express.static('public'));
//...
Ora ricarica l'app e dai un'occhiata alle dimensioni del bundle nel riquadro Rete.
Da 225 KB a 61,6 KB. In Response Headers
ora, un'intestazione content-encoding
mostra che il server sta inviando questo file codificato con gzip
.
Compressione statica
L'idea alla base della compressione statica è quella di comprimere e salvare gli asset in anticipo.
Pro
- La latenza dovuta a livelli di compressione elevati non è più un problema. Non è necessario comprimere i file al volo, in quanto ora possono essere recuperati direttamente.
Contro
- Gli asset devono essere compressi a ogni build. I tempi di compilazione possono aumentare in modo significativo se vengono utilizzati livelli di compressione elevati.
Compressione statica con Node/Express e webpack
Poiché la compressione statica prevede la compressione dei file in anticipo, le impostazioni di webpack possono essere modificate per comprimere gli asset durante la fase di build.
CompressionPlugin
può essere utilizzato a questo scopo.
Inizia aggiungendolo come devDependency
in package.json
:
"devDependencies": {
//...
"compression-webpack-plugin": "^1.1.11"
},
Come qualsiasi altro plug-in webpack, importalo nel file di configurazione,
webpack.config.js:
const path = require("path");
//...
const CompressionPlugin = require("compression-webpack-plugin");
e includilo nell'array plugins
:
module.exports = {
//...
plugins: [
//...
new CompressionPlugin()
]
}
Per impostazione predefinita, il plug-in comprime i file di build utilizzando gzip
. Consulta la documentazione per scoprire come aggiungere opzioni per utilizzare un algoritmo diverso o includere/escludere determinati file.
Quando l'app viene ricaricata e ricompilata, viene creata una versione compressa del bundle principale. Apri la console Glitch per dare un'occhiata ai contenuti della
directory public/
finale pubblicata dal server Node.
- Fai clic sul pulsante Strumenti.
- Fai clic sul pulsante Console.
- Nella console, esegui questi comandi per passare alla directory
public
e visualizzare tutti i relativi file:
cd public
ls
Anche la versione compressa con gzip del bundle, main.bundle.js.gz
, è ora salvata qui. CompressionPlugin
comprime anche index.html
per impostazione predefinita.
Il passaggio successivo consiste nel comunicare al server di inviare questi file
compressi con gzip ogni volta che vengono richieste le versioni JS originali. Questa operazione può essere eseguita definendo un nuovo percorso in server.js
prima che i file vengano pubblicati con express.static
.
const express = require('express'); const app = express(); app.get('*.js', (req, res, next) => { req.url = req.url + '.gz'; res.set('Content-Encoding', 'gzip'); next(); }); app.use(express.static('public')); //...
app.get
viene utilizzato per indicare al server come rispondere a una richiesta GET per un endpoint specifico. Viene quindi utilizzata una funzione di callback per definire come gestire questa
richiesta. Il percorso funziona nel seguente modo:
- Se specifichi
'*.js'
come primo argomento, questa operazione funziona per ogni endpoint attivato per recuperare un file JS. - All'interno del callback,
.gz
è collegato all'URL della richiesta e l'intestazione della rispostaContent-Encoding
è impostata sugzip
. - Infine,
next()
garantisce che la sequenza continui con qualsiasi callback che potrebbe essere il successivo.
Una volta ricaricata l'app, dai un'altra occhiata al riquadro Network
.
Come prima, una riduzione significativa delle dimensioni del bundle.
Conclusione
Questo codelab ha trattato il processo di minimizzazione e compressione del codice sorgente. Entrambe queste tecniche stanno diventando predefinite in molti degli strumenti disponibili oggi, quindi è importante scoprire se la tua toolchain le supporta già o se devi iniziare ad applicare entrambi i processi autonomamente.