Bundling e Minification

Best practice per velocizzare il caricamento del vostro sito

Ottimizzare. La parola chiave è questa.

Sempre più persone prediligono la navigazione sul web attraverso dispositivi mobili. È, quindi, molto importante che i siti web siano ottimizzati anche dal punto di vista della velocità di caricamento del sito stesso.
Se il nostro sito ha un caricamento lento, rischiamo di perdere potenziali clienti: aumenta, infatti, la probabilità che questi, estenuati dalla lunga attesa, abbandonino il sito per dedicarsi ad attività meno snervanti. Questo noi non lo vogliamo, giusto?

Una delle più efficaci soluzioni per velocizzare il caricamento del nostro sito, oltre a ottimizzare le immagini, è diminuire il numero e la dimensione di file CSS e JavaScript da scaricare.
Ricordiamoci che la maggior parte dei browser limita il numero di connessioni simultanee per ciascun host, quindi, quando le richieste raggiungono il numero massimo, le successive vengono accodate in attesa di essere elaborate.

Al giorno d’oggi un sito web è composto da molti file JavaScript e CSS: se da un lato questa è una pratica di sviluppo consolidata, bisogna considerare anche il risvolto della medaglia: più file equivalgono a più richieste che il client deve inviare al server andando a generare un maggior overhead di gestione.

Aprendo la console di Chrome, questo è ciò che si presenta quando viene effettuata una richiesta ad un sito web senza l’utilizzo di tecniche di ottimizzazione (vengono visualizzati solo file javascript e css):

Le richieste hanno un primo tempo di attesa (barra grigia), inviata la richiesta al server un ulteriore tempo di attesa prima che arrivi il primo byte della risorsa (barra verde) ed infine il tempo impiegato ad eseguire il download della risorsa stessa (barra blu). Rimando a questo link per una spiegazione più dettagliata del grafico.
Da notare che sono state effettuate 9 richieste in 386ms, per un totale di 2.2mb.

Qui sotto, invece, la stessa richiesta al sito ma con un occhio di riguardo alle ottimizzazioni:

Salta subito all’occhio un numero inferiore di richieste, in questo caso 5, ed anche un numero inferiore di megabyte trasferiti, per l’esattezza 566kb, il tutto in 162ms, un bel guadagno! Soprattutto considerando che l’esempio riportato è di una semplice applicazione MVC e, come ben sappiamo, le applicazioni che ogni giorno sviluppiamo sono ben più complesse.

Confrontando inoltre il codice HTML, qualcosa è cambiato:

1.

2.

I file javascript (lo stesso vale anche per i file css) si sono ridotti, cambiando anche nome!

Ma come si realizza la magia? La risposta è “Bundling e Minification”: si tratta di due tecniche che hanno lo scopo di ridurre il numero e la dimensione dei file scaricati dal browser, garantendo in questo modo un minore tempo di accesso al sito. Nello specifico:

  • Bundling: unisce più file per crearne uno solo, permettendo di diminuire il numero di richieste.
  • Minification: rimuove tutti i caratteri non necessari da un file (tab, spazi, commenti, ritorni a capo), abbrevia il nome delle variabili e genera alias “brevi” per le funzioni senza, ovviamente, modificarne la logica. Questo fa sì che le dimensioni del file stesso diminuiscano drasticamente.

A seconda di quale framework di sviluppo si decida di utilizzare, le strategie di Bundling e Minification vengono configurate in maniera differente. Vediamo come configurarle per entrambi i framework .NET: Full e Core.

.NET Full

ASP.NET MVC offre la possibilità di gestire il Bundling e la Minification grazie alla classe System.Web.Optimization.
Partendo da un progetto MVC, apriamo il file BundlingConfig.cs presente nella cartella App_Start. La classe presenta un metodo statico chiamato RegisterBundels, in cui andremo a scrivere questo codice:

using System.Web.Optimization;
namespace BundlingAndMinification
{
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/others").Include(
"~/Scripts/angular.js",
"~/Scripts/Chart.js",
"~/Scripts/custom.scripts.js"));
}
}
}

Il frammento di codice sopra aggiunge un bundle di script nel modulo bundle. Il nome che viene impostato è una directory virtuale. Si raccomanda di non utilizzare lo stesso nome sia per il bundle che per i file che compongono il bundle stesso.

Fatto questo è necessario registrare il modulo bundle (non è necessario registrare ogni singolo bundle che creeremo, ma solo il modulo).

using System.Web;
using System.Web.Optimization;

namespace BundlingAndMinification
{
public class MvcApplication : HttpApplication
{
protected void Application_Start()
{
// …

BundleConfig.RegisterBundles(BundleTable.Bundles);

// …
}
}
}

Per utilizzare il bundle appena creato, è necessario inserire nella pagina HTML la seguente riga di codice:

@Scripts.Render("~/bundles/others")

Per i file CSS, invece:

bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/bootstrap.css",
"~/Content/site.css"));

E, nella pagina HTML:

@Styles.Render("~/Content/css")

Niente di più facile!
In fase di debug IIS utilizzerà i file singoli, mentre, una volta in produzione, compilando l’applicativo in modalità “release” verranno generati i bundle. Assicuriamoci quindi che nel web.config la modalità di debug sia impostata a “true”, altrimenti verranno generati i bundle.

<system.web>
<compilation debug="true" targetyFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>

Al fine di testare il funzionamento del bundle senza modificare le opzioni di debug, basterà scrivere nella classe RegisterBundlings:

BundleTable.EnableOptimizations = true;

In questo modo avremmo la possibilità di effettuare il debug del codice e di avere i bundle generati, in quanto il codice scritto sopra non sovrascrive la modalità di debug (che rimane sempre a “true”) ma serve esclusivamente a generare i bundle.

Impostando quindi la proprietà a true, la modalità di debug rimarrà sempre attiva, ma avremmo la possibilità di avere abilitato il bundling e la minificazione per poter eseguire operazioni di debugging.

Convenzioni

È importante sapere come agisce di default il sistema di bundling e minification per evitare di impazzire in caso ci fosse qualche comportamento “inatteso”:

FILE GIÀ MINIFICATI
Se è presente un file già minificato (per convenzione il file deve avere il suffisso “.min” dopo il nome del file, quindi: myscript.js -> myscript.min.js), il sistema di minification utilizzerà quel file e non effettuerà l’operazione di minification sul file che voi avete aggiunto in configurazione.

VERSIONAMENTO
Alcuni file javascript hanno nel nome anche la versione. Se una libreria viene aggiornata, risulta scomodo cambiare a mano la configurazione dei bundling, aggiornando la versione. Per questo ci viene in aiuto il placeholder {version}. Grazie a questa keyword non è necessario specificare la versione del file: verrà incluso automaticamente a patto che rispetti la convenzione nel nome.

bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));

In questo modo, potremo avere inizialmente la versione 1.7 e successivamente basterà aggiornare il file alla versione 1.8, senza bisogno di cambiare il codice sopra; verrà eseguito il bundling della versione 1.8 in modo del tutto automatico.

CARICAMENTO FILE DA CDN
Per migliorare le performance è anche possibile scaricare il file da CDN (Content Delivery Network)

bundles.UseCdn = true;
var cdnPath = "http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js";
bundles.Add(new ScriptBundle("~/bundles/jquery", cdnPath));

Cache

Quando il browser richiede una risorsa, la prima azione che esegue è verificare se questa esiste già nella sua cache: se è già presente la utilizzerà, in caso contrario effettuerà la chiamata per scaricare la risorsa. Quindi, se aggiorniamo un file contenuto all’interno di un bundle, non avremo la certezza che queste modifiche raggiungano tutti i nostri utenti a meno che non venga eseguito un refresh forzato del browser da parte dell’utente stesso.

Per risolvere questo problema, il sistema di bundle ci vienein aiuto. Il bundle generato nella pagina HTML contiene un token, nel nostro caso:

Il token è generato automaticamente e cambia ogni qualvolta che uno dei file presenti nel bundle viene modificato. Questo fa sì che il nome della risorsa non sia più lo stesso (in quanto la porzione del token è stata modificata) e che, quindi, sia nuovamente scaricata.

.NET Core

.NET Core ha rivoluzionato tutto il meccanismo di bundle, di minification e la classe BundlingConfig ne ha subito le conseguenze, difatti non esiste più!
Essendo la modularità uno dei punti di forza di .NET Core, alcune funzionalità (come ad esempio bundle e minification) sono state rimosse delegando il tutto a strumenti terzi.

Dopo alcune ricerche ho deciso di mostrarvi questo tool, che ritengo valido per il nostro scopo:

WEB OPTIMIZER CORE
Questo pacchetto nuget creato da madskristensen permette di effettuare bundle e minification al runtime. Il funzionamento è molto semplice.

Una volta aggiunto il pacchetto alla soluzione, aggiungiamo nella classe ConfigureServices.cs

public void ConfigureServices(IServiceCollection services)
{
// …
services.AddWebOptimizer();
  // ...
}

e nella class Configure.cs

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// …
  app.UseWebOptimizer();
  app.UseStaticFiles();
  // ...
}

facendo attenzione ad aggiungere il codice prima del metodo UseStaticFiles(). Questo è necessario per far sì che l’applicazione utilizzi i file generati da web optimizer.

Il gioco è fatto! Se adesso andiamo a vedere i file css e javascript, questi saranno minificati.

Se volessimo minificare solo alcuni file, basterà scrivere quanto segue:

services.AddWebOptimizer(pipeline => 
{
pipeline.MinifyCssFiles("css/styleCore.css");
pipeline.MinifyCssFiles("css/styleRed.css");
});

Se invece vogliamo eseguire l’operazione di bundle, scriveremo:

services.AddWebOptimizer(pipeline => 
{
pipeline.AddCssBundle("css/mycssbundle.css", "css/merge1.css", "css/merge2.css");
});

La root di partenza è sempre wwwroot.

Ed inseriremo nella pagina HTML

<link rel="stylesheet" href="/css/mybundle.css" />

questo metodo, oltre ad eseguire il bundle, esegue anche la minizzazione.

Il tutto è valido sia per i file CSS che per i file JavaScript, utilizzando i relativi metodi.

La documentazione completa di Web Optimizer Core la trovate qui: https://github.com/ligershark/WebOptimizer/blob/master/README.md

Grazie alle tecniche di bundle e minification potrete far risparmiare tempo prezioso anche ai vostri utenti e mantenerli (spero) più tempo possibile sul vostro sito web.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *