Depuis ASP.Net MVC 4, pour augmenter les performances de son application, il est possible de mettre facilement en place du bundling et de la minification. Qu’est-ce que ces termes signifient ?
Le bundling va permettre de réduire le nombre de requêtes HTTP qui partent pour aller charger différentes ressources. D’autant plus que ce nombre de connexions simultannées peut être bridé sur certains navigateurs (voir http://www.browserscope.org/?category=network).
La capture d’écran ci dessous présente le traffic réseau généré par l’affichage du simple index de la page d’index du template de base d’un site mvc 4. On voit clairement que les css des différents control de jquery ui représentent un gros paquet de fichiers.

Le principe du bundling consiste donc à prendre tout ces fichiers et à les grouper dans un seul fichier. Pour nous développeurs, cela ne change rien, les fichiers restent séparés, c’est donc mieux organisé et plus simple à debugger, mais pour le consommateur de nos pages web, il n’y aura qu’un seul fichier à télécharger.
L’autre feature don’t je parlais en introduction de cet article concerne la minification. Le principe est simple, un script (javascript, css, ou autre), contient beaucoup de caractères qui ne sont pas nécessaires à sa bonne exécution (des commentaires, des espaces blancs etc.). Lorsque l’on minifie un script, on va donc enlever tout ces caractères et ne garder que l’essentiel, mais on va aussi renommer les différents éléments du code (variables, paramètres, etc.) avec des noms les plus courts possibles. Cela devient donc illisible pour un développeur, mais c’est beaucoup plus léger et donc beaucoup plus rapide à être téléchargé.
Pour commencer, il faut savoir que le bundling n’est activé que si l’application fonctionne sans débuggueur. Pour le tester, il va donc falloir faire un tour dans le web.config de l’application et le désactiver (ou faire un run Ctrl-F5).
<compilation debug="false" targetFramework="4.5" />
Ensuite, il faudra activer la propriété EnableOptimizations (personnellement, je le fais au début de mon BundleConfig).
BundleTable.EnableOptimizations = true;
L’appel d’un bundle, se fait ensuite de la manière suivante :
@Styles.Render("~/Content/themes/base/css")
Nous verrons un peu plus tard à quoi correspond cette url. Pour l’heure, il est temps de tester le site avec les outils de profiling d’un browser afin de voir le changement dans les ressources qui sont chargées.

Cette fois, on peut voir que le nombre de fichiers chargé a été énormement réduit. On constate notamment que les fichiers css de jquery ui ont été factorisés en un seul fichier css dont le poid fait approximativement le poid d’un seul fichier css non minifié.
Si on regarde le détail de ce fichier bundlé, on peut constater qu’il a été grandement remanié avant d’être retourné au navigateur.

Il est temps maintenant de regarder comment cela marche en détails…
Avec le template de base, la gestion des bundles se fait dans la classe BundleConfig de App_Start, sa méthode RegisterBundles sera automatiquement appelée dans le Application_Start. Il suffit alors de peupler la collection de bundle.
Les classes de bundle dépendent du type d’objet à bundler et à minifier. Pour du css, on utilisera un StyleBundle, pour du javascript, on utilisera un objet ScriptBundle. Dans les deux cas, ces classes dérivent de la classe Bundle. Chacun de ces types est lié à une collection de IBundleTransform qui seront appelés au moment de générer la réponse pour le client. Dans le cas de la classe StyleBundle, celle ci utilise une instance de CssMinify. Dans le cas de la classe ScriptBundle, elle utilise une instance de JsMinify. Ainsi, le modèle est totalement extensible, et on peut déjà trouver d’autres types de bundle et de transform liés pour du coffee script, less, etc.
Pour la création d’un bundle, le premier paramètre à passer est donc celui qui sera utilisé comme nom du bundle (et que l’on retrouvera dans @Styles.Render par exemple). Puis, dans la méthode Include, on pourra passer les n fichiers à inclure dans le 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 est également possible d’utiliser des wildcards, ainsi, l’exemple précédent pour également s’écrire de la manière suivante.
bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(
"~/Content/themes/base/*.css"));
Dans le cas de jquery, le token “{version}”, le moteur va donc automatiquement gérer le renvoie de la version actuelle de jquery, les fichiers de docs utilisés par l’intellisense sont automatiquement exclus, et les versions minifiés sont utilisés lorsque le débuggueur n’est pas attaché.
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
La classe bundle permet également la génération d’une clé uniquement à partir de son contenu. Celle ci sera utilisée pour la mise en cache du bundle côté client (la durée du cache est d’un an, et propre au user agent de chaque browser). A chaque modification d’un des fichiers du bundle, sa clé est modifiée et une nouvelle version pourra être téléchargée.

Enfin, le mécanisme de bundle permet de rediriger automatiquement vers un CDN. Il suffit d’activer l’option UseCdn sur la collection de bundle, puis de spécifier l’adresse du CDN pour le contenu concerné. L’exemple ci dessous concerne JQuery vers le CDN de Google. Si le mécanisme d’optimisations est activé, alors JQuery sera téléchargé depuis le CDN, sinon, il sera pris depuis la copie locale.
bundles.UseCdn = true;
const string jqueryCdnPath = "http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js";
bundles.Add(new ScriptBundle("~/bundles/jquery",
jqueryCdnPath).Include(
"~/Scripts/jquery-{version}.js"));
En espérant que ce post vous permettra d’améliorer les performances de vos applications web. A bientot !