Creazione di bundle e minimizzazione
La creazione di bundle e la minificazione sono due tecniche che è possibile usare in ASP.NET 4.5 per migliorare il tempo di caricamento delle richieste. La creazione di bundle e la minificazione migliorano il tempo di caricamento riducendo il numero di richieste al server e riducendo le dimensioni degli asset richiesti( ad esempio CSS e JavaScript).
La maggior parte dei principali browser attuali limita il numero di connessioni simultanee per ogni nome host a sei. Ciò significa che durante l'elaborazione di sei richieste, le richieste aggiuntive per gli asset in un host verranno accodate dal browser. Nell'immagine seguente, le schede di rete degli strumenti di sviluppo F12 di Internet Explorer mostrano la tempistica per gli asset richiesti dalla visualizzazione Informazioni su di un'applicazione di esempio.
Le barre grigie mostrano l'ora in cui la richiesta viene accodata dal browser in attesa del limite di sei connessioni. La barra gialla è il tempo di richiesta al primo byte, ovvero il tempo impiegato per inviare la richiesta e ricevere la prima risposta dal server. Le barre blu mostrano il tempo impiegato per ricevere i dati di risposta dal server. È possibile fare doppio clic su un asset per ottenere informazioni dettagliate sulla tempistica. Ad esempio, l'immagine seguente mostra i dettagli di intervallo per il caricamento del file /Scripts/MyScripts/JavaScript6.js .
L'immagine precedente mostra l'evento Start , che indica l'ora in cui la richiesta è stata accodata a causa del limite del browser per il numero di connessioni simultanee. In questo caso, la richiesta è stata accodata per 46 millisecondi in attesa del completamento di un'altra richiesta.
Impacchettare
La creazione di bundle è una nuova funzionalità di ASP.NET 4.5 che semplifica la combinazione o l'aggregazione di più file in un singolo file. È possibile creare pacchetti CSS, JavaScript e altri bundle. Un minor numero di file significa un minor numero di richieste HTTP e che può migliorare le prestazioni di caricamento della prima pagina.
L'immagine seguente mostra la stessa visualizzazione temporale della visualizzazione Informazioni mostrata in precedenza, ma questa volta con bundle e minification abilitati.
Minimizzazione
La minificazione esegue un'ampia gamma di ottimizzazioni del codice diverse per gli script o css, ad esempio la rimozione di spazi vuoti e commenti non necessari e la riduzione dei nomi delle variabili in un unico carattere. Si consideri la funzione JavaScript seguente.
AddAltToImg = function (imageTagAndImageID, imageContext) {
///<signature>
///<summary> Adds an alt tab to the image
// </summary>
//<param name="imgElement" type="String">The image selector.</param>
//<param name="ContextForImage" type="String">The image context.</param>
///</signature>
var imageElement = $(imageTagAndImageID, imageContext);
imageElement.attr('alt', imageElement.attr('id').replace(/ID/, ''));
}
Dopo la minificazione, la funzione viene ridotta al seguente:
AddAltToImg = function (n, t) { var i = $(n, t); i.attr("alt", i.attr("id").replace(/ID/, "")) }
Oltre a rimuovere i commenti e gli spazi vuoti non necessari, i parametri e i nomi delle variabili seguenti sono stati rinominati (abbreviati) come indicato di seguito:
Originale | Ridenominato |
---|---|
imageTagAndImageID | n |
imageContext | t |
imageElement | i |
Impatto di Bundling e Minification
La tabella seguente illustra diverse differenze importanti tra l'elenco di tutti gli asset singolarmente e l'uso di bundle e minification (B/M) nel programma di esempio.
Uso di B/M | Senza B/M | Modifica | |
---|---|---|---|
Richieste di file | 9 | 34 | 256% |
KB inviato | 3.26 | 11.92 | 266% |
KB ricevuto | 388.51 | 530 | 36% |
Tempo di caricamento | 510 MS | 780 MS | 53% |
I byte inviati hanno avuto una riduzione significativa con la creazione di bundle perché i browser sono piuttosto verbosi con le intestazioni HTTP applicate alle richieste. La riduzione dei byte ricevuti non è così grande perché i file più grandi (Scripts\jquery-ui-1.8.11.min.js e Scripts\jquery-1.7.1.min.js) sono già minimizzati. Nota: i tempi nel programma di esempio usavano lo strumento Fiddler per simulare una rete lenta. (Da Fiddler Menu Regole , selezionare Prestazioni e simulare velocità modem.
Debug di JavaScript in bundle e minimizzati
È facile eseguire il debug di JavaScript in un ambiente di sviluppo (in cui l'elemento di compilazione nel file Web.config è impostato su debug="true"
) perché i file JavaScript non sono raggruppati o minimizzati. È anche possibile eseguire il debug di una build di versione in cui i file JavaScript sono raggruppati e minimizzati. Usando gli strumenti di sviluppo F12 di Internet Explorer, è possibile eseguire il debug di una funzione JavaScript inclusa in un bundle minimizzato usando l'approccio seguente:
- Selezionare la scheda Script e quindi selezionare il pulsante Avvia debug .
- Selezionare il bundle contenente la funzione JavaScript di cui si vuole eseguire il debug usando il pulsante assets.
- Formattare il codice JavaScript minimizzato selezionando il pulsante Configurazione e quindi selezionando Formatta JavaScript.
- Nella casella di input Search Script (Input script di ricerca) selezionare il nome della funzione di cui si vuole eseguire il debug. Nell'immagine seguente, AddAltToImg è stato immesso nella casella di input Search Script .In the following image, AddAltToImg was entered in the Search Script input box.
Per altre informazioni sul debug con gli strumenti di sviluppo F12, vedere l'articolo MSDN Using the F12 Developer Tools to Debug JavaScript Errors .For more information on debugging with the F12 developer tools, see the MSDN article Using the F12 Developer Tools to Debug JavaScript Errors.
Controllo della creazione di bundle e della minificazione
La creazione di bundle e la minificazione sono abilitate o disabilitate impostando il valore dell'attributo debug nell'elemento di compilazione nel file Web.config. Nel codice XML debug
seguente, è impostato su true in modo che la creazione di bundle e la minificazione sia disabilitata.
<system.web>
<compilation debug="true" />
<!-- Lines removed for clarity. -->
</system.web>
Per abilitare la creazione di bundle e la minificazione, impostare il debug
valore su "false". È possibile eseguire l'override dell'impostazione Web.config con la EnableOptimizations
proprietà nella BundleTable
classe . Il codice seguente abilita la creazione di bundle e la minimizzazione ed esegue l'override di qualsiasi impostazione nel file Web.config .
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
// Code removed for clarity.
BundleTable.EnableOptimizations = true;
}
Nota
A meno che EnableOptimizations
non sia true
o l'attributo debug nell'elemento di compilazione nel file Web.config sia impostato su false
, i file non verranno aggregati o minimizzati. Inoltre, non verrà usata la versione min dei file, verranno selezionate le versioni di debug complete. EnableOptimizations
esegue l'override dell'attributo debug nell'elemento di compilazione nel file Web.config
Uso di bundle e minification con Web Forms ASP.NET e pagine Web
- Per le pagine Web, vedere la voce di blog Aggiunta dell'ottimizzazione Web a un sito di pagine Web.
- Per i Web Form, vedere la voce di blog Aggiunta di bundling e minification a Web Form.
Uso di Bundling e Minification con ASP.NET MVC
In questa sezione verrà creato un progetto MVC ASP.NET per esaminare la creazione di bundle e la minimizzazione. Creare prima di tutto un nuovo progetto Internet MVC ASP.NET denominato MvcBM senza modificare le impostazioni predefinite.
Aprire il file App\_Start\BundleConfig.cs ed esaminare il RegisterBundles
metodo usato per creare, registrare e configurare i bundle. Il codice seguente illustra una parte del RegisterBundles
metodo .
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
// Code removed for clarity.
}
Il codice precedente crea un nuovo bundle JavaScript denominato ~/bundles/jquery che include tutti gli elementi appropriati, ovvero debug o minimizzati ma non .file vsdoc) nella cartella Scripts che corrispondono alla stringa con caratteri jolly "~/Scripts /jquery-{version}.js". Per ASP.NET MVC 4, questo significa che con una configurazione di debug, il file jquery-1.7.1.js verrà aggiunto al bundle. In una configurazione di versione jquery-1.7.1.min.js verrà aggiunto. Il framework di creazione di bundle segue diverse convenzioni comuni, ad esempio:
- Selezionare il file ".min" per il rilascio quando FileX.min.js e FileX.js esistente.
- Selezione della versione non ".min" per il debug.
- Ignorando i file "-vsdoc", ad esempio jquery-1.7.1-vsdoc.js, usati solo da IntelliSense.
La {version}
corrispondenza con caratteri jolly mostrata in precedenza viene usata per creare automaticamente un bundle jQuery con la versione appropriata di jQuery nella cartella Scripts . In questo esempio, l'uso di un carattere jolly offre i vantaggi seguenti:
- Consente di usare NuGet per eseguire l'aggiornamento a una versione jQuery più recente senza modificare il codice di creazione di bundle o i riferimenti jQuery precedenti nelle pagine di visualizzazione.
- Seleziona automaticamente la versione completa per le configurazioni di debug e la versione ".min" per le build di versione.
Uso di una rete CDN
Il codice seguente sostituisce il bundle jQuery locale con un bundle jQuery della rete CDN.
public static void RegisterBundles(BundleCollection bundles)
{
//bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
// "~/Scripts/jquery-{version}.js"));
bundles.UseCdn = true; //enable CDN support
//add link to jquery on the CDN
var jqueryCdnPath = "https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js";
bundles.Add(new ScriptBundle("~/bundles/jquery",
jqueryCdnPath).Include(
"~/Scripts/jquery-{version}.js"));
// Code removed for clarity.
}
Nel codice precedente, jQuery verrà richiesto dalla rete CDN mentre è in modalità di rilascio e la versione di debug di jQuery verrà recuperata localmente in modalità di debug. Quando si usa una rete CDN, è necessario disporre di un meccanismo di fallback nel caso in cui la richiesta della rete CDN non riesca. Il frammento di markup seguente dalla fine del file di layout mostra lo script aggiunto alla richiesta jQuery in caso di esito negativo della rete CDN.
</footer>
@Scripts.Render("~/bundles/jquery")
<script type="text/javascript">
if (typeof jQuery == 'undefined') {
var e = document.createElement('script');
e.src = '@Url.Content("~/Scripts/jquery-1.7.1.js")';
e.type = 'text/javascript';
document.getElementsByTagName("head")[0].appendChild(e);
}
</script>
@RenderSection("scripts", required: false)
</body>
</html>
Creazione di un bundle
Il metodo della classe Include
Bundle accetta una matrice di stringhe, in cui ogni stringa è un percorso virtuale della risorsa. Il codice seguente del RegisterBundles
metodo nel file App\_Start\BundleConfig.cs mostra come vengono aggiunti più file a un bundle:
bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(
"~/Content/themes/base/jquery.ui.core.css",
"~/Content/themes/base/jquery.ui.resizable.css",
"~/Content/themes/base/jquery.ui.selectable.css",
"~/Content/themes/base/jquery.ui.accordion.css",
"~/Content/themes/base/jquery.ui.autocomplete.css",
"~/Content/themes/base/jquery.ui.button.css",
"~/Content/themes/base/jquery.ui.dialog.css",
"~/Content/themes/base/jquery.ui.slider.css",
"~/Content/themes/base/jquery.ui.tabs.css",
"~/Content/themes/base/jquery.ui.datepicker.css",
"~/Content/themes/base/jquery.ui.progressbar.css",
"~/Content/themes/base/jquery.ui.theme.css"));
Il metodo della classe IncludeDirectory
Bundle viene fornito per aggiungere tutti i file in una directory (e facoltativamente tutte le sottodirectory) che corrispondono a un criterio di ricerca. L'API della classe IncludeDirectory
Bundle è illustrata di seguito:
public Bundle IncludeDirectory(
string directoryVirtualPath, // The Virtual Path for the directory.
string searchPattern) // The search pattern.
public Bundle IncludeDirectory(
string directoryVirtualPath, // The Virtual Path for the directory.
string searchPattern, // The search pattern.
bool searchSubdirectories) // true to search subdirectories.
I bundle vengono a cui si fa riferimento nelle visualizzazioni usando il metodo Render , (Styles.Render
per CSS e Scripts.Render
per JavaScript). Il markup seguente del file Views\Shared\_Layout.cshtml mostra come le visualizzazioni predefinite ASP.NET progetto Internet fanno riferimento ai bundle CSS e JavaScript.
<!DOCTYPE html>
<html lang="en">
<head>
@* Markup removed for clarity.*@
@Styles.Render("~/Content/themes/base/css", "~/Content/css")
@Scripts.Render("~/bundles/modernizr")
</head>
<body>
@* Markup removed for clarity.*@
@Scripts.Render("~/bundles/jquery")
@RenderSection("scripts", required: false)
</body>
</html>
Si noti che i metodi Render accettano una matrice di stringhe, in modo da poter aggiungere più bundle in una riga di codice. In genere si vogliono usare i metodi Render che creano il codice HTML necessario per fare riferimento all'asset. È possibile usare il Url
metodo per generare l'URL dell'asset senza il markup necessario per fare riferimento all'asset. Si supponga di voler usare il nuovo attributo asincrono HTML5. Il codice seguente illustra come fare riferimento a modernizr usando il Url
metodo .
<head>
@*Markup removed for clarity*@
<meta charset="utf-8" />
<title>@ViewBag.Title - MVC 4 B/M</title>
<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<meta name="viewport" content="width=device-width" />
@Styles.Render("~/Content/css")
@* @Scripts.Render("~/bundles/modernizr")*@
<script src='@Scripts.Url("~/bundles/modernizr")' async> </script>
</head>
Utilizzo del carattere jolly "*" per selezionare i file
Il percorso virtuale specificato nel Include
metodo e il criterio di ricerca nel IncludeDirectory
metodo possono accettare un carattere jolly "*" come prefisso o suffisso nell'ultimo segmento di percorso. La stringa di ricerca non fa distinzione tra maiuscole e minuscole. Il IncludeDirectory
metodo ha la possibilità di eseguire ricerche nelle sottodirectory.
Si consideri un progetto con i file JavaScript seguenti:
- Scripts\Common\AddAltToImg.js
- Scripts\Common\ToggleDiv.js
- Scripts\Common\ToggleImg.js
- Scripts\Common\Sub1\ToggleLinks.js
La tabella seguente mostra i file aggiunti a un bundle usando il carattere jolly, come illustrato di seguito:
Call | File aggiunti o eccezioni generati |
---|---|
Include("~/Scripts/Common/*.js") | AddAltToImg.js, ToggleDiv.js, ToggleImg.js |
Include("~/Scripts/Common/T*.js") | Eccezione di modello non valida. Il carattere jolly è consentito solo sul prefisso o sul suffisso. |
Include("~/Scripts/Common/*og.*") | Eccezione di modello non valida. È consentito un solo carattere jolly. |
Include("~/Scripts/Common/T*") | ToggleDiv.js, ToggleImg.js |
Include("~/Scripts/Common/*") | Eccezione di modello non valida. Un segmento con caratteri jolly puro non è valido. |
IncludeDirectory("~/Scripts/Common", "T*") | ToggleDiv.js, ToggleImg.js |
IncludeDirectory("~/Scripts/Common", "T*", true) | ToggleDiv.js, ToggleImg.js, ToggleLinks.js |
L'aggiunta esplicita di ogni file a un bundle è in genere la scelta preferita rispetto al caricamento con caratteri jolly dei file per i motivi seguenti:
L'aggiunta di script per impostazione predefinita con caratteri jolly per caricarli in ordine alfabetico, che in genere non è quello desiderato. I file CSS e JavaScript devono essere spesso aggiunti in un ordine specifico (non alfabetico). È possibile ridurre questo rischio aggiungendo un'implementazione IBundleOrderer personalizzata, ma l'aggiunta esplicita di ogni file è meno soggetta a errori. Ad esempio, è possibile aggiungere nuovi asset a una cartella in futuro che potrebbe richiedere di modificare l'implementazione di IBundleOrderer .
È possibile includere file specifici aggiunti a una directory usando il caricamento con caratteri jolly in tutte le visualizzazioni che fanno riferimento a tale bundle. Se lo script specifico della visualizzazione viene aggiunto a un bundle, è possibile che venga visualizzato un errore JavaScript in altre visualizzazioni che fanno riferimento al bundle.
I file CSS che importano altri file comportano il caricamento dei file importati due volte. Ad esempio, il codice seguente crea un bundle con la maggior parte dei file CSS del tema dell'interfaccia utente jQuery caricati due volte.
bundles.Add(new StyleBundle("~/jQueryUI/themes/baseAll") .IncludeDirectory("~/Content/themes/base", "*.css"));
Il selettore di caratteri jolly "*.css" porta in ogni file CSS nella cartella, incluso il file Content\themes\base\jquery.ui.all.css . Il file jquery.ui.all.css importa altri file CSS.
Memorizzazione nella cache dei bundle
I bundle impostano l'intestazione HTTP Expires un anno da quando viene creato il bundle. Se si passa a una pagina visualizzata in precedenza, Fiddler mostra che Internet Explorer non effettua una richiesta condizionale per il bundle, ovvero non sono presenti richieste HTTP GET da Internet Explorer per i bundle e nessuna risposta HTTP 304 dal server. È possibile forzare Internet Explorer a effettuare una richiesta condizionale per ogni bundle con la chiave F5 (con conseguente risposta HTTP 304 per ogni bundle). È possibile forzare un aggiornamento completo usando ^F5 (con conseguente risposta HTTP 200 per ogni bundle).
L'immagine seguente mostra la scheda Memorizzazione nella cache del riquadro di risposta di Fiddler:
Richiesta.
http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81
è per il bundle AllMyScripts e contiene una coppia di stringhe di query v=r0sLDicvP58AIXN\_mc3QdyVvVj5euZNzdsa2N1PKvb81. La stringa di query v ha un token di valore che è un identificatore univoco usato per la memorizzazione nella cache. Finché il bundle non cambia, l'applicazione ASP.NET richiederà il bundle AllMyScripts usando questo token. Se un file nel bundle cambia, il framework di ottimizzazione ASP.NET genererà un nuovo token, garantendo che le richieste del browser per il bundle ottengano l'aggregazione più recente.
Se si eseguono gli strumenti di sviluppo F12 di Internet Explorer 9 e si passa a una pagina caricata in precedenza, Internet Explorer visualizza erroneamente le richieste GET condizionali effettuate a ogni bundle e il server che restituisce HTTP 304. È possibile leggere perché Internet Explorer 9 presenta problemi per determinare se è stata effettuata una richiesta condizionale nel post di blog Uso di reti CDN e scadenza per migliorare le prestazioni del sito Web.
LESS, CoffeeScript, SCSS, Sass Bundling.
Il framework di creazione di bundle e minification fornisce un meccanismo per elaborare linguaggi intermedi come SCSS, Sass, LESS o Coffeescript e applicare trasformazioni come la minificazione al bundle risultante. Ad esempio, per aggiungere file con estensione less al progetto MVC 4:
Creare una cartella per il contenuto LESS. Nell'esempio seguente viene utilizzata la cartella Content\MyLess .
Aggiungere il pacchetto NuGet con estensione less senza punti al progetto.
Aggiungere una classe che implementa l'interfaccia IBundleTransform . Per la trasformazione .less, aggiungere il codice seguente al progetto.
using System.Web.Optimization; public class LessTransform : IBundleTransform { public void Process(BundleContext context, BundleResponse response) { response.Content = dotless.Core.Less.Parse(response.Content); response.ContentType = "text/css"; } }
Creare un bundle di file LESS con
LessTransform
e la trasformazione CssMinify . Aggiungere il codice seguente alRegisterBundles
metodo nel file App\_Start\BundleConfig.cs .var lessBundle = new Bundle("~/My/Less").IncludeDirectory("~/My", "*.less"); lessBundle.Transforms.Add(new LessTransform()); lessBundle.Transforms.Add(new CssMinify()); bundles.Add(lessBundle);
Aggiungere il codice seguente a tutte le visualizzazioni che fanno riferimento al bundle LESS.
@Styles.Render("~/My/Less");
Considerazioni sul bundle
Una buona convenzione da seguire quando si creano bundle consiste nell'includere "bundle" come prefisso nel nome del bundle. In questo modo si evita un possibile conflitto di routing.
Dopo aver aggiornato un file in un bundle, viene generato un nuovo token per il parametro della stringa di query bundle e il bundle completo deve essere scaricato alla successiva richiesta da parte di un client di una pagina contenente il bundle. Nel markup tradizionale in cui ogni asset è elencato singolarmente, verrà scaricato solo il file modificato. Gli asset che cambiano spesso potrebbero non essere buoni candidati per la creazione di bundle.
La creazione di bundle e la minimizzazione migliorano principalmente il tempo di caricamento della prima richiesta di pagina. Dopo aver richiesto una pagina Web, il browser memorizza nella cache gli asset (JavaScript, CSS e immagini) in modo che la creazione di bundle e la minificazione non forniscano alcun miglioramento delle prestazioni quando si richiede la stessa pagina o pagine nello stesso sito che richiede gli stessi asset. Se non si imposta correttamente l'intestazione di scadenza per gli asset e non si usa la creazione di bundle e la minificazione, l'euristica di aggiornamento dei browser contrassegnerà gli asset non aggiornati dopo alcuni giorni e il browser richiederà una richiesta di convalida per ogni asset. In questo caso, la creazione di bundle e la minimizzazione forniscono un aumento delle prestazioni dopo la prima richiesta di pagina. Per informazioni dettagliate, vedere il blog Uso di reti CDN e scadenza per migliorare le prestazioni del sito Web.
La limitazione del browser di sei connessioni simultanee per ogni nome host può essere mitigata tramite una rete CDN. Poiché la rete CDN avrà un nome host diverso rispetto al sito di hosting, le richieste di asset dalla rete CDN non verranno conteggiati rispetto al limite di sei connessioni simultanee all'ambiente di hosting. Una rete CDN può anche offrire vantaggi comuni per la memorizzazione nella cache dei pacchetti e la memorizzazione nella cache perimetrale.
I bundle devono essere partizionati in base alle pagine che ne hanno bisogno. Ad esempio, il modello MVC ASP.NET predefinito per un'applicazione Internet crea un bundle di convalida jQuery separato da jQuery. Poiché le visualizzazioni predefinite create non hanno input e non inseriscono valori, non includono il bundle di convalida.
Lo System.Web.Optimization
spazio dei nomi viene implementato in System.Web.Optimization.dll. Sfrutta la libreria WebGrease (WebGrease.dll) per le funzionalità di minificazione, che a sua volta usa Antlr3.Runtime.dll.
Uso Twitter per creare post rapidi e condividere collegamenti. Il mio handle twitter è: @RickAndMSFT
Risorse aggiuntive
- Video:Bundling e Ottimizzazione di Howard Dierking
- Aggiunta dell'ottimizzazione Web a un sito di pagine Web.
- Aggiunta di bundle e minification ai Web Form.
- Implicazioni sulle prestazioni di Bundling e Minification on Web Browsing di Henrik F Nielsen @frystyk
- Uso di reti CDN e scadenza per migliorare le prestazioni del sito Web di Rick Anderson @RickAndMSFT
- Ridurre al minimo il tempo di RTT (round trip times)
Collaboratori
- Hao Kung
- Howard Dierking
- Diana LaRose