Bundle en .NET

En este post veremos como generar «paquetes» o bundles con ficheros de js y css para tener una web mas óptima en MVC .Net

¿Qué es?

Un bundle es una utilidad en .Net para agrupar ficheros y hacer menos peticiones al servidor, pero también minifica ficheros para que ocupen menos y aparte, puede guardar en cache los ficheros para no descargarlos en cada página. Para esto último, imaginemos que tenemos un script que se ejecuta en la master para hacer x, pues en lugar de que cada vez que nos movamos de pantalla se recargue, la guarda y solo se bajaría los concretos de esa página. Solo se la descargaría si han pasado x minutos desde que se la bajo y hemos llamado a una página que la contenga.

Otra cosa buena que tiene, es que podemos hacer llamadas a recursos versionados como JQuery, que los scripts son por ejemplo jquery-1.10.2.js; o jquery-2.1.1.js; nos permite poner algo así jquery-{version}.js y así si actualizamos las librerías, no nos afectaría a la aplicación.

Configurándolo

Primero lo que necesitamos es una aplicación web, para ver como están montados los bundles podemos verlo en la que viene de ejemplo en MVC, pero crearemos una nueva o usar alguna que tengais. En el Global.asax, dentro de la función de Application_Start, deberemos asegurarnos de que tenemos esta linea (si no la tenemos al añadimos):

BundleConfig.RegisterBundles(BundleTable.Bundles);

Después, nos vamos al web.config y dentro del tag system.web tenemos que tener o añadir lo siguiente:

<compilation debug="true" />

De esta manera el bundle no hará nada… lo tenemos que poner a false.
Miramos si tenemos un fichero en App_Start que se llama BundleConfig, si no lo creamos y tiene que contener lo siguiente:

using System.Web.Optimization;

namespace WebApplication2
{
    public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
           //Aquí irán nuestra referencia a scripts y styles
        }
    }
}

En la function de registerBundles activar esta propiedad.

BundleTable.EnableOptimizations = true;

Pero cuando estamos desarrollando, se nos haría muy engorroso que todos los js estuviesen minificados y/o ofuscados, de tal manera que lo ponemos así:

#if !Debug
BundleTable.EnableOptimizations = true;

De esta manera cuando estemos en la configuración de «debug» nos mantendrá todos los scripts y estilos de la manera original (sin minificación ni ofuscación) y cuando compilemos o despleguemos con otra configuración (por ejemplo release) estará activado.

Utilizándolo

Bueno y ahora, como se utiliza. Primero necesitaremos crear un script y un ficheros de estilos, para ello los agregamos en los directorios de Scripts y Content respectivamente (esto es un ejemplo, podéis ponerlos en un directorio personalizado si queréis porque no afecta). Yo he creado MiScript.js, MiScript2.js, MisEstilos.css y MisEstilos2.css.

Después nos vamos a el fichero BundleConfig y en la función de RegisterBundles y agregamos lo siguiente:

#region Scripts
bundles.Add(new ScriptBundle("~/js/Gerenal").Include(
            "~/Scripts/MiScript.js",
            "~/Scripts/MiScript2.js"));

#endregion
#region Styles
bundles.Add(new StyleBundle("~/css/General").Include(
            "~/Content/MisEstilos.css",
            "~/Content/MisEstilos2.css"));

#endregion

Los he puesto en 2 regions porque siempre acaba creciendo y así esta más organizado. Aquí podemos ver que en el constructor de ScriptBundle y StyleBundle le tenemos que pasar un string, solo es un alias, no es un fichero físico y no tiene que coincidir con ninguno porque sino no lo resuelve bien, por eso le he puesto un directorio que no existe como es js y css. Recalco mucho esto porque es fácil equivocarse, en el proyecto donde estoy nos paso.

Después del constructor añadimos con un include o varios los ficheros que queramos agregar a ese alias. Cuando compilemos en release nos aparecerá en el navegador un ~/js/General con un número de version al final con el conjunto de scripts y otro con el de css.

Estos 2 tienes los alias de General (porque lo eh puesto asi, repito que es un alias solo) para cargar los ficheros que compartan todas las Views por ejemplo. Si queremos añadir más lo haríamos tal que:

#region Scripts
bundles.Add(new ScriptBundle("~/js/Gerenal").Include(
            "~/Scripts/MiScript.js",
            "~/Scripts/MiScript2.js"));

bundles.Add(new ScriptBundle("~/js/Usuarios").Include(
            "~/Scripts/Usuarios.js");
#endregion
#region Styles
bundles.Add(new StyleBundle("~/css/General").Include(
            "~/Content/MisEstilos.css",
            "~/Content/MisEstilos2.css"));

bundles.Add(new StyleBundle("~/css/General").Include(
            "~/Content/Usuarios.css"));
#endregion

Ahora si lo ejecutásemos en release, nos parecerían 2 de scripts, uno de General y el otro de Usuarios, y 2 de css.

Cuando tengo un fichero de styles con una ruta a una imagen no me aparece

Un problema con los bundles son las rutas de las imagenes, pero tiene su solucion. Los scripts que tengan imágenes se tendrán que poner de esta manera:

bundles.Add(new StyleBundle("~/Styles/Login")
   .Include("~/Content/FileWithImage1.css", new CssRewriteUrlTransformWrapper())
   .Include("~/Content/FileWithImage2.css", new CssRewriteUrlTransformWrapper());

Y añadimos esto al final del BundleConfig:

 private class CssRewriteUrlTransformWrapper : IItemTransform
        {
            public string Process(string includedVirtualPath, string input)
            {
                return new CssRewriteUrlTransform().Process("~" + VirtualPathUtility.ToAbsolute(includedVirtualPath), input);
            }
        }

Teneis una explicación más detallada aquí