asp.net-mvc podríamos - ¿Cómo puedo comprimir y minimizar automáticamente los archivos JavaScript en una aplicación ASP.NET MVC?




código qué (8)

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

Me gustaría saber si existe una forma automatizada para comprimir y minimizar esas 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


Answers

En realidad, hay una forma mucho más sencilla de usar proyectos de implementación web (WDP). El WDP administrará las complejidades de las herramientas 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>

Utiliza la tarea JSCompress MSBuild de http://msbuildtasks.tigris.org/ que creo que está basada en JSMin.

La idea es dejar todos sus archivos js tal como están (es decir, depurables / legibles para el ser humano) . Cuando construye su WDP, primero copiará los archivos js a OutputPath y luego se llamará al objetivo 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. A continuación, implemente 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 permitir que el WDP maneje la creación del directorio virtual, solo 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: usando MSBuild y Team Foundation Build


Personalmente creo que mantener los archivos separados durante el desarrollo es invaluable y que durante la producción es cuando algo como esto cuenta. Así que modifiqué mi script de implementación para hacer eso más 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 utilizo:

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 construcción toma todos los archivos en la sección y los combina todos juntos. Luego utilizo el minificador de YUI para obtener una versión reducida del javascript. Como IIS lo proporciona, prefiero activar la compresión en IIS para obtener la compresión de gzip. **** Añadido **** Mi script de despliegue es un script de MSBuild. También uso las excelentes tareas de comunidad de MSBuild ( http://msbuildtasks.tigris.org/ ) para ayudar a implementar una aplicación.

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

La siguiente sección ejecutará la compilación en el compilador asp.net para copiar la aplicación a la unidad de destino. (En un paso anterior, simplemente ejecuto comandos de uso net net y mapeo 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 construir / t: apuntar cada vez que necesito implementarlo en un entorno.


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


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


Puede usar MvcContrib.IncludeHandling. Eso:

  • Admite CSS y JS
  • Se combina con una sola solicitud
  • Minifica
  • Compresas Gzip / desinflar
  • Configura encabezados de caché
  • Utiliza los métodos de extensión HTMLHelper para registrar includes para luego combinarlos en tiempo de ejecución
  • Es conectable a través de IoC

Debajo de las cubiertas, usa YUICompressor.


Scott Hanselman publicó recientemente un blog sobre cómo combinar y mover scripts a archivos estáticos , básicamente utilizando ScriptManager con referencias CompositeScript y un atributo de 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 minimizar los archivos estáticos, probablemente tenga que (y debería) usar herramientas de minificación en el tiempo de compilación / despliegue.


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, al llamar a Html.RenderCss, se combinan todos los estilos / js en un solo archivo y se minimiza.

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

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

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

La misma forma 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 guiones cuando se establece, lo que es muy útil.

En cuestión de minutos, pasé del puntaje Yslow de F a B. (24 guiones hasta 2) ... ¡Increíble! 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é el script combinado durante un período definido, lo que aliviará esto rápidamente.


Aquí está mi ejemplo de trabajo:

[HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Create(Product product, HttpPostedFileBase file)
    {
        if (!ModelState.IsValid)
            return PartialView("Create", product);
        if (file != null)
        {

            var fileName = Path.GetFileName(file.FileName);
            var guid = Guid.NewGuid().ToString();
            var path = Path.Combine(Server.MapPath("~/Content/Uploads/ProductImages"), guid + fileName);
            file.SaveAs(path);
            string fl = path.Substring(path.LastIndexOf("\\"));
            string[] split = fl.Split('\\');
            string newpath = split[1];
            string imagepath = "Content/Uploads/ProductImages/" + newpath;
            using (MemoryStream ms = new MemoryStream())
            {
                file.InputStream.CopyTo(ms);
                byte[] array = ms.GetBuffer();
            }
            var nId = Guid.NewGuid().ToString();
            // Save record to database
            product.Id = nId;
            product.State = 1;
            product.ImagePath = imagepath;
            product.CreatedAt = DateTime.Now;
            db.Products.Add(product);
            await db.SaveChangesAsync();
            TempData["message"] = "ProductCreated";

            //return RedirectToAction("Index", product);
        }
        // after successfully uploading redirect the user
        return Json(new { success = true });
    }




javascript asp.net-mvc compression extjs minimize