Bundles en ASP.NET MVC4

¡Buenas! Este va a ser un post cortito, sobre los Bundles en ASP.NET MVC. Los bundles es el mecanismo que tiene ASP.NET MVC para incluir varios ficheros (de script o css) que están relacionados entre ellos.

Si os creáis un proyecto de ASP.NET MVC4 nuevo (sin que sea la plantilla Empy, claro) veréis el siguiente código en la página de Layout:

   1: <!DOCTYPE html>

   2: <html>

   3: <head>

   4:     <meta charset="utf-8" />

   5:     <meta name="viewport" content="width=device-width" />

   6:     <title>@ViewBag.Title</title>

   7:     @Styles.Render("~/Content/themes/base/css", "~/Content/css")

   8:     @Scripts.Render("~/bundles/modernizr")

   9: </head>

  10: <body>

  11:     @RenderBody()

  12:  

  13:     @Scripts.Render("~/bundles/jquery")

  14:     @RenderSection("scripts", required: false)

  15: </body>

  16: </html>

En lugar de incluir directamente las etiquetas <link> (para las CSS) o <script> para referenciar a archivos js externos, lo que tenemos son llamadas a Styles.Render y Scripts.Render.

El parámetro que se pasa a esos métodos Render es el nombre del bundle a renderizar. ¿Y donde están definidos los bundles? Pues en el archivo App_Start/BundleConfig tenéis el siguiente método:

   1: public static void RegisterBundles(BundleCollection bundles)

   2: {

   3:     bundles.Add(new ScriptBundle("~/bundles/jquery").Include(

   4:                 "~/Scripts/jquery-1.*"));

   5:  

   6:     bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(

   7:                 "~/Scripts/jquery-ui*"));

   8:  

   9:     bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(

  10:                 "~/Scripts/jquery.unobtrusive*",

  11:                 "~/Scripts/jquery.validate*"));

  12:  

  13:     bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(

  14:                 "~/Scripts/modernizr-*"));

  15:  

  16:     bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"));

  17:  

  18:     bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(

  19:                 "~/Content/themes/base/jquery.ui.core.css",

  20:                 "~/Content/themes/base/jquery.ui.resizable.css",

  21:                 "~/Content/themes/base/jquery.ui.selectable.css",

  22:                 "~/Content/themes/base/jquery.ui.accordion.css",

  23:                 "~/Content/themes/base/jquery.ui.autocomplete.css",

  24:                 "~/Content/themes/base/jquery.ui.button.css",

  25:                 "~/Content/themes/base/jquery.ui.dialog.css",

  26:                 "~/Content/themes/base/jquery.ui.slider.css",

  27:                 "~/Content/themes/base/jquery.ui.tabs.css",

  28:                 "~/Content/themes/base/jquery.ui.datepicker.css",

  29:                 "~/Content/themes/base/jquery.ui.progressbar.css",

  30:                 "~/Content/themes/base/jquery.ui.theme.css"));

  31: }

Dicho método, que es llamado desde el Application_Start, es donde se asocia cada bundle con un conjunto de archivos (fijaos que se puede usar * para indicar un conjunto de archivos).

Así vemos que el bundle ~/bundles/modernizr se compone de los archivos que estén en ~/scripts y cuyo nombre empiece por mondernizr-

Esto en tiempo de ejecución no hace nada especial, la verdad. Si abrimos cualquier vista y le damos a “ver código fuente” lo que vemos es simplemente el conjunto de tags <link> o <script>:

   1: <link href="/Content/themes/base/jquery.ui.core.css" rel="stylesheet" type="text/css" />

   2: <link href="/Content/themes/base/jquery.ui.resizable.css" rel="stylesheet" type="text/css" />

   3: <link href="/Content/themes/base/jquery.ui.selectable.css" rel="stylesheet" type="text/css" />

   4: <link href="/Content/themes/base/jquery.ui.accordion.css" rel="stylesheet" type="text/css" />

   5: <link href="/Content/themes/base/jquery.ui.autocomplete.css" rel="stylesheet" type="text/css" />

   6: <link href="/Content/themes/base/jquery.ui.button.css" rel="stylesheet" type="text/css" />

   7: <link href="/Content/themes/base/jquery.ui.dialog.css" rel="stylesheet" type="text/css" />

   8: <link href="/Content/themes/base/jquery.ui.slider.css" rel="stylesheet" type="text/css" />

   9: <link href="/Content/themes/base/jquery.ui.tabs.css" rel="stylesheet" type="text/css" />

  10: <link href="/Content/themes/base/jquery.ui.datepicker.css" rel="stylesheet" type="text/css" />

  11: <link href="/Content/themes/base/jquery.ui.progressbar.css" rel="stylesheet" type="text/css" />

  12: <link href="/Content/themes/base/jquery.ui.theme.css" rel="stylesheet" type="text/css" />

  13: <link href="/Content/site.css" rel="stylesheet" type="text/css" />

  14:  

  15: <script src="/Scripts/modernizr-2.0.6-development-only.js" type="text/javascript"></script>

Pero lo bueno de usar los bundles no es tan solo poder agrupar lógicamente nuestros archivos javascript o css. Lo bueno es que estamos a una sola propiedad de distancia de poder agruparlos y minificarlos (pésima palabreja lo sé, pero soy incapaz de encontrar una traducción para minification).

Así, si en el Application_Start añadimos la línea:

   1: BundleTable.EnableOptimizations = true;

Si ahora ejecutamos y miramos el código fuente veremos lo siguiente:

   1: <link href="/Content/themes/base/css?v=UM624qf1uFt8dYtiIV9PCmYhsyeewBIwY4Ob0i8OdW81" rel="stylesheet" type="text/css" /> 

   2: <link href="/Content/css?v=ZAyqul2u_47TBTYq93se5dXoujE0Bqc44t3H-kap5rg1" rel="stylesheet" type="text/css" /> 

   3: <script src="/bundles/modernizr?v=EuTZa4MRY0ZqCYpBXj_MhJfFJU2QBDf0xGrV_p1fHME1" type="text/javascript"></script>

Podemos ver como tenemos tan solo dos tags <link> y uno <script> en lugar de todos los tags de antes. Ahora cada Bundle ha generado un solo tag. Eso, ya de por sí es bueno, ya que se reduce el número de peticiones http (una de las cosas que más tardan a la hora de cargar una página web es ir abriendo y cerrando conexiones http).

Pero no solo eso, sino que además ahora el contenido está “minificado”, es decir se han eliminado todos los espacios y saltos de línea redundantes y se renombran todas las variables y nombres de función posibles para que tengan una longitud menor, de forma que el tamaño final sea menor, lo que a su vez redunda en un mejor rendimiento.

Y todo ello tan solo habilitando la EnableOptimizations.

Un par de curiosidades…

Hoy en día muchas librerías js incluyen dos .js: uno sin normal y el otro minificado. Usualmente (por una de esas convenciones que terminan siendo estándares de facto) el archivo minificado tiene el sufijo “min”. Pongamos a jQuery como ejemplo. Si te lo descargas verás dos archivos como p.ej.

  1. jquery-1.6.2.js
  2. jquery-1.6.2.min.js

El primero es el archivo .js normal (para usar en debug p.ej.) y el otro es el archivo minificado. Funcionalmente ambos son equivalentes pero el primero ocupa unas 230K y el segundo unas 91.

Pues bien, imaginad que tenemos un bundle para jQuery, definido de la siguiente forma:

   1: bundles.Add(new ScriptBundle("~/bundles/jquery")

   2: .Include("~/Scripts/jquery-1.6.2*"));

Fijaos que es un bundle que incluiría cualquier archivo js cuyo nombre empezase por jquery-1.6.2. Y en nuestro directorio ~/Scripts tenemos dos archivos que empiezan por este nombre, jquery-1.6.2.js y jquery-1.6.2.min.js. Pues bien el bundle es lo suficientemente inteligente como:

  • Generar tan solo el tag <script> para el archivo jquery-1.6.2.js en el caso de que las optimizaciones estén deshabilitadas (y no generar el tag para el archivo jquery-1.6.2.min.js).
  • Retornar el contenido del archivo jquery-1.6.2.min.js en el caso de que las optimizaciones estén habilitadas. Aunque ASP.NET igualmente minifica el propio archivo (aunque ya esté minificado). Es decir coje el archivo jquery-1.6.2.min.js, lo minifica y devuelve el contenido. Debo reconocer que me ha sorprendido que minifique de nuevo el contenido, pero es es lo que hace.

De hecho en el propio Solution Explorer de VS2012 ya se aprecia que VS “sabe” que ambos archivos están relacionados:

image

Espero que este post os haya resultado de interés! 😉

Saludos!

8 comentarios sobre “Bundles en ASP.NET MVC4”

  1. Hola Eduard:
    Magnífico post!
    Una pregunta. ¿El parámetro v es siempre igual si los ficheros no cambian? Lo digo por la caché local del navegador. Asumo que sí, pero tú dirás.
    Un saludo y gracias.

  2. Hola, he leído tu post, me parece muy bueno.

    Te cuento, Al subir los archivos al servidor, o sea al desplegarlos, pareciera que no vuelve a generar la compresión de los css o js, y no me actualiza los archivos, cómo podré solucionar esto.

    Saludos

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *