net mvc minification example bundling asp and c# javascript css asp.net-mvc minify

c# - minification - Concatenar y minificar JavaScript sobre la marcha O en tiempo de compilación-ASP.NET MVC



bundling asp net mvc (8)

¿Por qué no usar el ScriptManager? Aquí hay un MVCScriptManager que combinará AND squish.

Como una extensión a esta pregunta aquí Vinculación de bibliotecas de JavaScript en los controles de usuario Estaba buscando algunos ejemplos de cómo las personas concatenan y minimizan JavaScript sobre la marcha O en tiempo de compilación. También me gustaría ver cómo funciona en tus páginas maestras.

No me importa que los archivos específicos de la página se minimicen y se vinculen de forma individual como son actualmente (ver a continuación), pero todos los archivos JavaScript en la página maestra principal (tengo alrededor de 5 o 6) me gustaría concatenarlos y minificarlos.

¡Puntos de bonificación para cualquier persona que también incorpore concatenación y minificación de CSS! :-)

Página maestra actual con los archivos JavaScript comunes que me gustaría concatenar y minificar:

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %> <head runat="server"> ... BLAH ... <asp:ContentPlaceHolder ID="AdditionalHead" runat="server" /> ... BLAH ... <%= Html.CSSBlock("/styles/site.css") %> <%= Html.CSSBlock("/styles/jquery-ui-1.7.1.css") %> <%= Html.CSSBlock("/styles/jquery.lightbox-0.5.css") %> <%= Html.CSSBlock("/styles/ie6.css", 6) %> <%= Html.CSSBlock("/styles/ie7.css", 7) %> <asp:ContentPlaceHolder ID="AdditionalCSS" runat="server" /> </head> <body> ... BLAH ... <%= Html.JSBlock("/scripts/jquery-1.3.2.js", "/scripts/jquery-1.3.2.min.js") %> <%= Html.JSBlock("/scripts/jquery-ui-1.7.1.js", "/scripts/jquery-ui-1.7.1.min.js") %> <%= Html.JSBlock("/scripts/jquery.validate.js", "/scripts/jquery.validate.min.js") %> <%= Html.JSBlock("/scripts/jquery.lightbox-0.5.js", "/scripts/jquery.lightbox-0.5.min.js") %> <%= Html.JSBlock("/scripts/global.js", "/scripts/global.min.js") %> <asp:ContentPlaceHolder ID="AdditionalJS" runat="server" /> </body>

Usado en una página como esta (con la que estoy contento):

<asp:Content ID="signUpContent" ContentPlaceHolderID="AdditionalJS" runat="server"> <%= Html.JSBlock("/scripts/pages/account.signup.js", "/scripts/pages/account.signup.min.js") %> </asp:Content>


ACTUALIZACIÓN: Recomendaciones por ahora (finales de 2013):

Vería en Microsoft ASP.NET integrado en Bundling and Minification .



Esto es lo que he usado para concatenar, comprimir y almacenar en caché archivos CSS y JS: http://gist.github.com/130913

Solo requiere Yahoo.Yui.Compressor.dll en el directorio bin. No se comprime en tiempo de compilación, pero los archivos se almacenan en caché con una dependencia de archivo, por lo que solo se cargan una vez, hasta que se modifiquen.

Luego solo agrego este código en <head>:

<link rel="stylesheet" type="text/css" href="/YuiCompressor.ashx?css=reset,style,etc" />

y esto justo antes del </ body>:

<script type="text/javascript" src="/YuiCompressor.ashx?js=main,other,etc"></script>

Está diseñado para trabajar con múltiples archivos, todos en la misma ruta, pero podría actualizarse fácilmente para admitir diferentes rutas.


Prueba esto:

Recientemente completé un poco de investigación y desarrollo consecuente en el trabajo que va bastante lejos para mejorar el rendimiento del front-end de nuestra aplicación web. Pensé que compartiría la solución básica aquí.

La primera cosa obvia que hacer es comparar su sitio usando YSlow de Yahoo y PageSpeed ​​de Google. Estos resaltarán las mejoras en el rendimiento de "fruta a la altura" que se deben hacer. A menos que ya lo haya hecho, las sugerencias resultantes casi seguramente incluirán la combinación, minificación y gzip de su contenido estático.

Los pasos que vamos a realizar son:

Escriba un HTTPHandler personalizado para combinar y minificar CSS. Escriba un HTTPHandler personalizado para combinar y minificar JS. Incluye un mecanismo para garantizar que lo anterior solo haga su magia cuando la aplicación no está en modo de depuración. Escriba un control web personalizado del lado del servidor para mantener fácilmente la inclusión del archivo css / js. Habilite GZIP de ciertos tipos de contenido en IIS 6. Bien, comencemos con CSSHandler.asax que implementa la interfaz .NET IHttpHandler:

using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Web; namespace WebApplication1 { public class CssHandler : IHttpHandler { public bool IsReusable { get { return true; } } public void ProcessRequest(HttpContext context) { string[] cssFiles = context.Request.QueryString["cssfiles"].Split('',''); List<string> files = new List<string>(); StringBuilder response = new StringBuilder(); foreach (string cssFile in cssFiles) { if (!cssFile.EndsWith(".css", StringComparison.OrdinalIgnoreCase)) { //log custom exception context.Response.StatusCode = 403; return; } try { string filePath = context.Server.MapPath(cssFile); string css = File.ReadAllText(filePath); string compressedCss = Yahoo.Yui.Compressor.CssCompressor.Compress(css); response.Append(compressedCss); } catch (Exception ex) { //log exception context.Response.StatusCode = 500; return; } } context.Response.Write(response.ToString()); string version = "1.0"; //your dynamic version number context.Response.ContentType = "text/css"; context.Response.AddFileDependencies(files.ToArray()); HttpCachePolicy cache = context.Response.Cache; cache.SetCacheability(HttpCacheability.Public); cache.VaryByParams["cssfiles"] = true; cache.SetETag(version); cache.SetLastModifiedFromFileDependencies(); cache.SetMaxAge(TimeSpan.FromDays(14)); cache.SetRevalidation(HttpCacheRevalidation.AllCaches); } } }

Ok, ahora algo de explicación:

Propiedad IsReUsable:

No nos ocupamos de nada específico de la instancia, lo que significa que podemos reutilizar de forma segura la misma instancia del manejador para tratar múltiples solicitudes, porque nuestra ProcessRequest es segura para el hilo. Más información.

Método ProcessRequest:

Nada demasiado agitado pasando aquí. Estamos revisando los archivos CSS que se nos dieron (consulte el CSSControl a continuación para saber cómo están ingresando) y comprimiendo cada uno, utilizando un puerto .NET del YUICompressor de Yahoo, antes de agregar los contenidos al flujo de respuesta saliente.

El resto del método se refiere a la configuración de algunas propiedades de almacenamiento en caché HTTP para optimizar aún más la forma en que el cliente del navegador descarga (o no, según el caso) el contenido.

Configuramos Etags en código para que puedan ser iguales en todas las máquinas de nuestra granja de servidores. Configuramos las dependencias de respuesta y caché en nuestros archivos reales, por lo que, en caso de que se reemplacen, la caché se invalidará. Configuramos Cacheability de modo que los proxies puedan almacenar en caché. VaryByParams utilizamos nuestro atributo cssfiles, de modo que podamos almacenar en caché por grupo de archivos CSS enviado a través del controlador. Y aquí está el CSSControl, un control personalizado del lado del servidor que hereda .NET LiteralControl.

Frente:

<customcontrols:csscontrol id="cssControl" runat="server"> <CustomControls:Stylesheet File="main.css" /> <CustomControls:Stylesheet File="layout.css" /> <CustomControls:Stylesheet File="formatting.css" /> </customcontrols:csscontrol>

Espalda:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Web; using System.Web.UI; using System.Linq; using TTC.iTropics.Utilities; namespace WebApplication1 { [DefaultProperty("Stylesheets")] [ParseChildren(true, "Stylesheets")] public class CssControl : LiteralControl { [PersistenceMode(PersistenceMode.InnerDefaultProperty)] public List<Stylesheet> Stylesheets { get; set; } public CssControl() { Stylesheets = new List<Stylesheet>(); } protected override void Render(HtmlTextWriter output) { if (HttpContext.Current.IsDebuggingEnabled) { const string format = "<link rel=/"Stylesheet/" href=/"stylesheets/{0}/"></link>"; foreach (Stylesheet sheet in Stylesheets) output.Write(format, sheet.File); } else { const string format = "<link type=/"text/css/" rel=/"Stylesheet/" href=/"stylesheets/CssHandler.ashx?cssfiles={0}&version={1}/"/>"; IEnumerable<string> stylesheetsArray = Stylesheets.Select(s => s.File); string stylesheets = String.Join(",", stylesheetsArray.ToArray()); string version = "1.00" //your version number output.Write(format, stylesheets, version); } } } public class Stylesheet { public string File { get; set; } } }

HttpContext.Current.IsDebuggingEnabled está conectado a la siguiente configuración en su web.config:

<system.web> <compilation debug="false"> </system.web>

Entonces, básicamente, si su sitio está en modo de depuración, obtiene un marcado HTML como este:

<link rel="Stylesheet" href="stylesheets/formatting.css"></link> <link rel="Stylesheet" href="stylesheets/layout.css"></link <link rel="Stylesheet" href="stylesheets/main.css"></link>

Pero si estás en modo de producción (debug = false), obtendrás un marcado como este:

<link type="text/css" rel="Stylesheet" href="CssHandler.ashx?cssfiles=main.css,layout.css,formatting.css&version=1.0"/>

Este último invocará obviamente CSSHandler, que se encargará de combinar, minimizar y almacenar en caché su contenido CSS estático.

Todo lo anterior también puede duplicarse para su contenido de JavaScript estático:

`JSHandler.ashx:

using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Web; namespace WebApplication1 { public class JSHandler : IHttpHandler { public bool IsReusable { get { return true; } } public void ProcessRequest(HttpContext context) { string[] jsFiles = context.Request.QueryString["jsfiles"].Split('',''); List<string> files = new List<string>(); StringBuilder response = new StringBuilder(); foreach (string jsFile in jsFiles) { if (!jsFile.EndsWith(".js", StringComparison.OrdinalIgnoreCase)) { //log custom exception context.Response.StatusCode = 403; return; } try { string filePath = context.Server.MapPath(jsFile); files.Add(filePath); string js = File.ReadAllText(filePath); string compressedJS = Yahoo.Yui.Compressor.JavaScriptCompressor.Compress(js); response.Append(compressedJS); } catch (Exception ex) { //log exception context.Response.StatusCode = 500; return; } } context.Response.Write(response.ToString()); string version = "1.0"; //your dynamic version number here context.Response.ContentType = "application/javascript"; context.Response.AddFileDependencies(files.ToArray()); HttpCachePolicy cache = context.Response.Cache; cache.SetCacheability(HttpCacheability.Public); cache.VaryByParams["jsfiles"] = true; cache.VaryByParams["version"] = true; cache.SetETag(version); cache.SetLastModifiedFromFileDependencies(); cache.SetMaxAge(TimeSpan.FromDays(14)); cache.SetRevalidation(HttpCacheRevalidation.AllCaches); } } }

Y su JSControl que lo acompaña:

Frente:

<customcontrols:JSControl ID="jsControl" runat="server"> <customcontrols:Script File="jquery/jquery-1.3.2.js" /> <customcontrols:Script File="main.js" /> <customcontrols:Script File="creditcardpayments.js" /> </customcontrols:JSControl>

Espalda:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Web; using System.Web.UI; using System.Linq; namespace WebApplication1 { [DefaultProperty("Scripts")] [ParseChildren(true, "Scripts")] public class JSControl : LiteralControl { [PersistenceMode(PersistenceMode.InnerDefaultProperty)] public List<Script> Scripts { get; set; } public JSControl() { Scripts = new List<Script>(); } protected override void Render(HtmlTextWriter writer) { if (HttpContext.Current.IsDebuggingEnabled) { const string format = "<script src=/"scripts//{0}/"></script>"; foreach (Script script in Scripts) writer.Write(format, script.File); } else { IEnumerable<string> scriptsArray = Scripts.Select(s => s.File); string scripts = String.Join(",", scriptsArray.ToArray()); string version = "1.0" //your dynamic version number const string format = "<script src=/"scripts/JsHandler.ashx?jsfiles={0}&version={1}/"></script>"; writer.Write(format, scripts, version); } } } public class Script { public string File { get; set; } } }

Habilitando GZIP:

Como dice Jeff Atwood, habilitar Gzip en su servidor de sitio web es una obviedad. Después de un rastreo, decidí habilitar Gzip en los siguientes tipos de archivos:

.css .js .axd (archivos Javascript de Microsoft) .aspx (contenido habitual de formularios web ASP.NET) .ashx (Nuestros controladores) Para habilitar la compresión HTTP en su servidor web IIS 6.0:

Abra IIS, haga clic con el botón secundario en Sitios web, pestaña Servicios, active Comprimir archivos de aplicación y Comprimir archivos estáticos Detenga IIS Abra la metabase de IIS en el Bloc de notas (C: / WINDOWS / system32 / inetsrv / MetaBase.xml) y realice una copia de seguridad si lo hace Re nervioso sobre estas cosas Ubique y sobrescriba los dos elementos IIsCompressionScheme y uno IIsCompressionSchemes con lo siguiente:

¡Y eso es! Esto nos ahorró montones de ancho de banda y dio como resultado una aplicación web más receptiva en todo momento.

¡Disfrutar!


Recomendaría http://www.RequestReduce.com, que minimiza y combina css y javascript, así como las imágenes de fondo sprites css y optimiza su compresión PNG. Hace todo esto en tiempo de ejecución y almacena en caché la salida. No requiere código o configuración más allá de agregar HttpModule. Sirve todo el contenido almacenado en caché con encabezados de futuro lejano optimizados y ETags para garantizar que los navegadores guarden en caché los css / javascript / sprites el mayor tiempo posible. Si bien no requiere configuración, es altamente configurable y puede configurarse para ejecutarse con un CDN y sincronizar los archivos almacenados en caché en una granja de servidores web.

Todos los javascript, imágenes y css se obtienen a través de HTTP, por lo que puede incluir css y js de terceros y también es una excelente manera de minimizar / combinar recursos .axd como WebResource.axd y ScriptResource.axd. Determina la presencia de js y css a través del tipo de contenido, por lo que el recurso de destino puede tener cualquier extensión (o ninguna). Se ejecuta en cualquier tecnología basada en IIS, incluidas todas las versiones y motores de vista de MVC, formularios web y "páginas web".

Puede descargar desde http://www.RequestReduce.com , Nuget o tenedor desde https://github.com/mwrock/RequestReduce .


Rejuicer es un gran nuevo minificador para ASP.NET que está recibiendo mucha atención: http://rejuice.me

Está configurado como un módulo HTTP y realiza la minificación en tiempo de ejecución (una vez) y almacena en caché la salida.

Eso:

  • Tiene una interfaz fluida para la configuración
  • Le permite especificar archivos para minificar con reglas de comodines
  • Se ejecuta en Windows Azure
  • Algo mágicamente se apaga solo en entornos de desarrollo, por lo que puedes depurar tu código original de JavaScript (no minificado).

La configuración (hecha en ApplicationStart en global.asax.cs) es tan simple como:

OnRequest.ForJs("~/Combined.js") .Compact .FilesIn("~/Scripts/") .Matching("*.js") .Cache .Configure();


Use el Compresor YUI o el compresor Dojo. Ambos usan el motor de análisis de Rhino JS que tokeniza su código y, por lo tanto, solo funcionará si el código es válido. Si hay un error, se lo harán saber (¡que es una buena bonificación de IMO!) Packer, por otro lado, empacará su código incluso si contiene errores.

Uso YUI en todos mis proyectos a través de scripts de compilación. Nunca lo haga sobre la marcha, lleva demasiado tiempo hacer la compresión. Tanto YUI como Dojo están basados ​​en Java (ala Rhino) y si lo haces sobre la marcha, estarás generando procesos en segundo plano para generar la salida, lo cual no es bueno para el rendimiento. Siempre hazlo en tiempo de compilación.


Utilizo una solución personalizada basada en MSBuild y Microsoft Ajax Minifier. Gran parte de las publicaciones de blog existentes no manejan correctamente ciertos casos, como la integración con la compilación TFS.

Para cada proyecto web, creamos un archivo "wpp.targets" para extender el Pipeline de Publicación Web. Por ejemplo, si el proyecto es "Website.csproj", cree un archivo llamado "Website.wpp.targets" en el proyecto.

Coloque el siguiente código en el archivo de objetivos:

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="$(MSBuildExtensionsPath32)/PATH TO YOUR MSBUILD MINIFY TARGETS" /> <!-- Hook up minification task to WPP build process --> <PropertyGroup> <OnAfterPipelineTransformPhase> $(OnAfterPipelineTransformPhase); MinifyResourceFiles; </OnAfterPipelineTransformPhase> </PropertyGroup> <!-- Define temporary location to store minified resources --> <PropertyGroup> <MinifyResourceIntermediateOutput Condition="''$(MinifyResourceIntermediateOutput)''==''''">MinifyResourceFiles</MinifyResourceIntermediateOutput> <MinifyResourceIntermediateLocation Condition="''$(MinifyResourceIntermediateLocation)''==''''">$(_WPPDefaultIntermediateOutputPath)$(MinifyResourceIntermediateOutput)</MinifyResourceIntermediateLocation> </PropertyGroup> <Target Name="MinifyResourceFiles" DependsOnTargets="PipelineCollectFilesPhase" Condition="''$(Configuration)'' == ''Release''"> <!-- Create lists of the resources to minify --> <!-- These extract all Javascript and CSS files from the publishing pipeline "FilesForPackagingFromProject" and create two new lists. The "MinifiedFile" metadata on each item contains the temporary location where the minified file will be stored --> <ItemGroup> <JavaScriptToMinify Include="@(FilesForPackagingFromProject)" Condition="''%(FilesForPackagingFromProject.Extension)'' == ''.js''"> <MinifiedFile>$(MinifyResourceIntermediateLocation)/minified/%(DestinationRelativePath)</MinifiedFile> </JavaScriptToMinify> <StylesheetToMinify Include="@(FilesForPackagingFromProject)" Condition="''%(FilesForPackagingFromProject.Extension)'' == ''.css''"> <MinifiedFile>$(MinifyResourceIntermediateLocation)/minified/%(DestinationRelativePath)</MinifiedFile> </StylesheetToMinify> </ItemGroup> <!-- Minify resources --> <!-- These commands should be replaced with the MSBuild Tasks used to perform your minification I use my own custom tasks based on the Microsoft Ajax Minifier DLL The input of the minifier takes a source file directly from the project and outputs to a temporary location --> <MinifyJavaScript SourceFiles="@(JavaScriptToMinify)" DestinationFiles="@(JavaScriptToMinify->''%(MinifiedFile)'')" Comments="None" /> <MinifyStylesheet SourceFiles="@(StylesheetToMinify)" DestinationFiles="@(StylesheetToMinify->''%(MinifiedFile)'')" Comments="None" /> <!-- Remove the original source files from the packaging system and include the new minfied resources from the temporary location --> <ItemGroup> <!--Remove unminified resources from the pipeline --> <FilesForPackagingFromProject Remove="@(JavaScriptToMinify)" Condition="''@(JavaScriptToMinify)'' != ''''" /> <FilesForPackagingFromProject Remove="@(StylesheetToMinify)" Condition="''@(StylesheetToMinify)'' != ''''" /> <!--Add the minified resources at the new loction to the pipeline --> <FilesForPackagingFromProject Include="@(JavaScriptToMinify->''%(MinifiedFile)'')" Condition="''@(JavaScriptToMinify)'' != ''''"/> <FilesForPackagingFromProject Include="@(StylesheetToMinify->''%(MinifiedFile)'')" Condition="''@(StylesheetToMinify)'' != ''''"/> </ItemGroup> </Target> </Project>

La condición "''$ (Configuración'') == ''Versión''" en el objetivo de minificación se puede modificar según sus necesidades. Automatizará (y validará) automáticamente todos los archivos CSS y JS en el proyecto al momento de publicar, empacar y construir en el servidor.

Es posible que deba habilitar el destino "CopyWebApplication" de WPP para compilaciones de servidor. Para hacer esto, establezca la propiedad MSWalk UseWP_CopyWebApplication en True y PipelineDependsOnBuild en False. Los configuramos en el archivo de proyecto, antes de que se incluya el archivo de objetivos de la aplicación web.