¿Cómo puedo comprimir y minimizar automáticamente los archivos JavaScript en una aplicación ASP.NET MVC?

StackOverflow https://stackoverflow.com/questions/1224292

Pregunta

Así que tengo una aplicación ASP.NET MVC que hace referencia a varios archivos javascript en varios lugares (en el sitio maestro y referencias adicionales en varias vistas también).

Me gustaría saber si hay una forma automática de comprimir y minimizar tales referencias en un solo archivo .js cuando sea posible. Tal que esto ...

<script src="<%= ResolveUrl("~") %>Content/ExtJS/Ext.ux.grid.GridSummary/Ext.ux.grid.GridSummary.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ExtJS/ext.ux.rating/ext.ux.ratingplugin.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ExtJS/ext-starslider/ext-starslider.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ExtJS/ext.ux.dollarfield.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ExtJS/ext.ux.combobox.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ExtJS/ext.ux.datepickerplus/ext.ux.datepickerplus-min.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ExtJS/SessionProvider.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ExtJS/TabCloseMenu.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ActivityViewer/ActivityForm.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ActivityViewer/UserForm.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ActivityViewer/SwappedGrid.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ActivityViewer/Tree.js" type="text/javascript"></script>

... podría reducirse a algo como esto ...

<script src="<%= ResolveUrl("~") %>Content/MyViewPage-min.js" type="text/javascript"></script>

Gracias

¿Fue útil?

Solución

Personalmente, creo que mantener los archivos separados durante el desarrollo es invaluable y que durante la producción es cuando algo así cuenta. Así que modifiqué mi script de implementación para hacer eso arriba.

Tengo una sección que dice:

<Target Name="BeforeDeploy">

        <ReadLinesFromFile File="%(JsFile.Identity)">
            <Output TaskParameter="Lines" ItemName="JsLines"/>
        </ReadLinesFromFile>

        <WriteLinesToFile File="Scripts\all.js" Lines="@(JsLines)" Overwrite="true"/>

        <Exec Command="java -jar tools\yuicompressor-2.4.2.jar Scripts\all.js -o Scripts\all-min.js"></Exec>

    </Target>

Y en mi archivo de página maestra uso:

if (HttpContext.Current.IsDebuggingEnabled)
   {%>
    <script type="text/javascript" src="<%=Url.UrlLoadScript("~/Scripts/jquery-1.3.2.js")%>"></script>
    <script type="text/javascript" src="<%=Url.UrlLoadScript("~/Scripts/jquery-ui-1.7.2.min.js")%>"></script>
    <script type="text/javascript" src="<%=Url.UrlLoadScript("~/Scripts/jquery.form.js")%>"></script>
    <script type="text/javascript" src="<%=Url.UrlLoadScript("~/Scripts/jquery.metadata.js")%>"></script>
    <script type="text/javascript" src="<%=Url.UrlLoadScript("~/Scripts/jquery.validate.js")%>"></script>
    <script type="text/javascript" src="<%=Url.UrlLoadScript("~/Scripts/additional-methods.js")%>"></script>
    <script type="text/javascript" src="<%=Url.UrlLoadScript("~/Scripts/form-interaction.js")%>"></script>
    <script type="text/javascript" src="<%=Url.UrlLoadScript("~/Scripts/morevalidation.js")%>"></script>
    <script type="text/javascript" src="<%=Url.UrlLoadScript("~/Scripts/showdown.js") %>"></script>
<%
   }  else  {%> 
  <script type="text/javascript" src="<%=Url.UrlLoadScript("~/Scripts/all-min.js")%>"></script>
<% } %>

El script de compilación toma todos los archivos de la sección y los combina todos. Luego uso el minificador de YUI para obtener una versión reducida del javascript. Debido a que esto es servido por IIS, preferiría activar la compresión en IIS para obtener la compresión gzip. **** Adicional **** Mi script de implementación es un script de MSBuild. También utilizo las excelentes tareas de la comunidad MSBuild ( http://msbuildtasks.tigris.org/ ) para ayudar a implementar un aplicación.

No voy a publicar mi archivo de script completo aquí, pero aquí hay algunas líneas relevantes para demostrar la mayor parte de lo que hace.

La siguiente sección ejecutará la compilación en el compilador asp.net para copiar la aplicación en la unidad de destino. (En un paso anterior, simplemente ejecuté los comandos de uso de red de Exec y mapeé una unidad compartida de red).

<Target Name="Precompile" DependsOnTargets="build;remoteconnect;GetTime">

        <MakeDir Directories="%(WebApplication.SharePath)\$(buildDate)" />

        <Message Text="Precompiling Website to %(WebApplication.SharePath)\$(buildDate)" />

        <AspNetCompiler
            VirtualPath="/%(WebApplication.VirtualDirectoryPath)"
            PhysicalPath="%(WebApplication.PhysicalPath)"
            TargetPath="%(WebApplication.SharePath)\$(buildDate)"
            Force="true"
            Updateable="true"
            Debug="$(Debug)"
            />
        <Message Text="copying the correct configuration files over" />

        <Exec Command="xcopy $(ConfigurationPath) %(WebApplication.SharePath)\$(buildDate) /S /E /Y" />

     </Target>

Después de copiar todos los proyectos de solución, ejecuto esto:

    <Target Name="_deploy">
        <Message Text="Removing Old Virtual Directory" />
        <WebDirectoryDelete
            VirtualDirectoryName="%(WebApplication.VirtualDirectoryPath)"
            ServerName="$(IISServer)"
            ContinueOnError="true"
            Username="$(username)"  
            HostHeaderName="$(HostHeader)"
            />

        <Message Text="Creating New Virtual Directory" />

        <WebDirectoryCreate 
            VirtualDirectoryName="%(WebApplication.VirtualDirectoryPath)" 
            VirtualDirectoryPhysicalPath="%(WebApplication.IISPath)\$(buildDate)"
            ServerName="$(IISServer)"
            EnableDefaultDoc="true"
            DefaultDoc="%(WebApplication.DefaultDocument)"
            Username="$(username)"
            HostHeaderName="$(HostHeader)"
            />
</Target>

Eso debería ser suficiente para comenzar a automatizar la implementación. Puse todo esto en un archivo separado llamado Aspnetdeploy.msbuild. Acabo de msbuild / t: Destino cada vez que necesito implementarlo en un entorno.

Otros consejos

En realidad, hay una manera mucho más fácil de usar Proyectos de implementación web (WDP). El WDP gestionará las complejidades de la herramienta aspnet__compiler y aspnet__merge . Puede personalizar el proceso mediante una interfaz de usuario dentro de Visual Studio.

En cuanto a la compresión de los archivos js, puede dejar todos sus archivos js en su lugar y simplemente comprimir estos archivos durante el proceso de compilación. Entonces en el WDP declararías algo como esto:

<Project>
   REMOVE CONTENT HERE FOR WEB

<Import 
  Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<!-- Extend the build process -->
<PropertyGroup>
  <BuildDependsOn>
    $(BuildDependsOn);
    CompressJavascript
  </BuildDependsOn>
</PropertyGroup>

<Target Name="CompressJavascript">
  <ItemGroup>
    <_JSFilesToCompress Include="$(OutputPath)Scripts\**\*.js" />
  </ItemGroup>
  <Message Text="Compresing Javascript files" Importance="high" />
  <JSCompress Files="@(_JSFilesToCompress)" />
</Target>
</Project>

Esto utiliza la tarea JSCompress MSBuild de la MSBuild Community Tasks que creo que se basa en JSMin.

La idea es dejar todos los archivos js como están (es decir, depurables / legibles para humanos) . Cuando construya su WDP, primero copiará los archivos js en OutputPath y luego se llamará al destino CompressJavascript para minimizar los archivos js. Esto no modifica sus archivos fuente originales, solo los que están en la carpeta de salida del proyecto WDP. Luego despliega los archivos en la ruta de salida de WDP, que incluye el sitio precompilado. Cubrí este escenario exacto en mi libro (enlace debajo de mi nombre) .

También puede dejar que el WDP maneje la creación del Directorio Virtual, simplemente marque una casilla de verificación y complete el nombre del directorio virtual.

Para algunos enlaces en MSBuild:

Sayed Ibrahim Hashimi

Mi libro: Dentro del motor de compilación de Microsoft: Uso de MSBuild y Team Foundation Build

Scott Hanselman escribió recientemente en su blog sobre combinar y mover secuencias de comandos a archivos estáticos , básicamente utilizando el ScriptManager con referencias CompositeScript y un atributo Path :

<asp:ScriptManager runat="server">
    <CompositeScript path="http://www.example.com/1.js">
        <Scripts>
            <asp:ScriptReference />
            <asp:ScriptReference />
            <!-- etc. -->
        </Scripts>
    </CompositeScript>
</asp:ScriptManager>

En términos de minificar los archivos estáticos, es probable que deba (y deba) usar herramientas de minificación en el momento de la compilación / implementación.

MvcContrib.IncludeHandling funciona bien para esta situación. En el ejemplo, tengo un modelo con una colección de estilos (cadena). Además, si necesito agregar un estilo / JS personalizado a la página, también puedo hacerlo. Luego, llamar a Html.RenderCss combina todos los estilos / js en un solo archivo y lo minimiza.

<head>
<% foreach (var styleSheet in Model.Styles) {%>
<% Html.IncludeCss(styleSheet)); 

<% } %>
<% Html.IncludeCss("~/Scripts/jquery.1.4.2.js")); 

<%= Html.RenderCss() %>
</head>

La misma manera para javascript.

<%
Html.IncludeJs("~/scripts/ConsoleLogger.js");
Html.IncludeJs("~/scripts/jquery.log.js");
Html.IncludeJs("~/Scripts/2010.1.416/jquery.validate.min.js");
Html.IncludeJs("~/Scripts/2010.1.416/telerik.calendar.min.js");
Html.IncludeJs("~/Scripts/2010.1.416/telerik.datepicker.js");
Html.IncludeJs("~/scripts/jquery.ui.datepicker-en-GB.js");
%>
   

Cuando esto se procesa para el cliente, la salida se ve así (archivo combinado 1 minificado)

<link rel='stylesheet' type='text/css' href='/include/css/-QdUg9EnX5mpI0e4aKAaOySIbno%40'/>

La API también ofrece un indicador de depuración que, cuando está activado, no minimiza ni combina los scripts cuando se configura, lo cual es muy útil.

En cuestión de minutos pasé del puntaje Yslow de F a B. (24 guiones hasta 2) ... ¡Impresionante! Y una gota de 40kbs.

La desventaja obvia es que el servidor está haciendo la compresión sobre la marcha. Creo que hay opciones para almacenar en caché la secuencia de comandos combinada durante un período definido, lo que podría aliviar esto rápidamente.

Como otros han sugerido, sería mejor crear una compilación minificada estática. Alternativamente, hay una versión de YUICompressor disponible para .NET aquí: http://www.codeplex.com/YUICompressor

Puede usar MvcContrib.IncludeHandling. Se:

  • Soporta CSS y JS
  • Combina a una sola solicitud
  • Minizes
  • Compresas Gzip / Deflate
  • Configura encabezados de caché
  • Utiliza métodos de extensión HTMLHelper para registrar incluye para luego combinarlos en tiempo de ejecución
  • Se puede conectar a través de IoC

Debajo de las cubiertas, usa YUICompressor.

He escrito algo para manejar esto automáticamente. Utiliza el compilador de cierre de google. Puedes leer el código aquí:

http://www.picnet.com.au/blogs/Guido/post/2009/12/10/Javascript-runtime-compilation-using-AspNet-and-Googles-Closure-Compiler .aspx

Gracias

Guido

Sé que esta es una vieja pregunta, pero surgió primero ya que estaba haciendo algunas búsquedas de minificación. Recomendaría usar Gruntjs, http://gruntjs.com . Es una herramienta de compilación web completa.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top