【问题标题】:How can I automatically compress and minimize JavaScript files in an ASP.NET MVC app?如何在 ASP.NET MVC 应用程序中自动压缩和最小化 JavaScript 文件?
【发布时间】:2010-11-16 11:51:37
【问题描述】:

所以我有一个 ASP.NET MVC 应用程序,它在不同的地方引用了许多 javascript 文件(在站点主文件中以及在几个视图中的其他引用)。

我想知道是否有一种自动化方法可以将此类引用压缩和最小化到一个 .js 文件中。这样……

<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>

...可以简化成这样的...

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

谢谢

【问题讨论】:

标签: javascript asp.net-mvc compression extjs minimize


【解决方案1】:

我个人认为,在开发过程中将文件分开是非常宝贵的,而在生产过程中,这样的事情很重要。因此,我修改了我的部署脚本以执行上述操作。

我有一段写着:

<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>

在我的母版页文件中,我使用:

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>
<% } %>

构建脚本获取该部分中的所有文件并将它们组合在一起。然后我使用 YUI 的缩小器来获取 javascript 的缩小版本。因为这是由 IIS 提供的,所以我宁愿在 IIS 中打开压缩来获得 gzip 压缩。 **** 添加 **** 我的部署脚本是一个 MSBuild 脚本。我还使用出色的 MSBuild 社区任务 (http://msbuildtasks.tigris.org/) 来帮助部署应用程序。

我不打算在这里发布我的整个脚本文件,但这里有一些相关的行应该演示它的大部分功能。

以下部分将在 asp.net 编译器中运行构建,以将应用程序复制到目标驱动器。 (在上一步中,我只运行 exec net use 命令并映射网络共享驱动器)。

<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>

复制完所有解决方案项目后,我运行以下命令:

    <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>

这应该足以让您开始自动化部署。我将所有这些东西放在一个名为 Aspnetdeploy.msbuild 的单独文件中。每当我需要部署到环境时,我只需要 msbuild /t:Target。

【讨论】:

  • 我喜欢你的解决方案,但我不确定我是否完全理解它。 1.什么是Url.UrlLoadScript()?默认情况下,我看不到此方法可用。 2.你们的自动化部署流程是怎样的?我仍然通过简单地进行发布构建然后右键单击项目并选择“发布”来做这个老派。所以我不确定如何设置它,也不知道你的部署脚本如何适应它。你介意在这里更深入一点吗?谢谢
  • 我投票赞成 :) 这正是我们在流程中使用我们的 CI 解决方案作为自定义构建任务所做的。对于您的 #2 的答案,请查看 MSBuild 选项并创建构建脚本。当您“发布”时,您实际上是在执行构建脚本(所有 .proj 和 .sln 实际上都是 msbuild 脚本)。因此,他创建了一个构建任务,可以像我们一样通过 CI 解决方案执行,也可以通过拦截一些构建事件从 Visual stuido 执行。
  • 哎呀...哈哈,这是一种扩展方法。它与 Url.Content 基本相同,只是我在末尾放了一个 http 修订号。修订号应该更新为 SVN 修订号,所以它保持唯一。
  • 尽管我不确定从哪里开始使用 MSBuild,但我还是会将其标记为正确答案。这些 msbuild 脚本是进入一个单独的项目还是只是坐在某个独立的地方?任何推荐的 MSBuild 入门教程/操作方法?再次感谢!
  • 我只是将 MSBuild 脚本放在一个单独的文件中。我真的不记得实际上阅读过任何关于 MS Build 的教程。通过阅读大量关于它的 MSDN 文档,我有点理解了。我会先从概述开始。 msdn.microsoft.com/en-us/library/ms171452.aspx
【解决方案2】:

实际上使用Web Deployment Projects (WDP) 有一个更简单的方法。 WDP 将管理 aspnet__compileraspnet__merge 工具的复杂性。您可以通过 Visual Studio 内的 UI 自定义流程。

至于压缩 js 文件,您可以保留所有 js 文件并在构建过程中压缩这些文件。因此,在 WDP 中,您会声明如下内容:

<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>

这使用来自MSBuild Community Tasks 的 JSCompress MSBuild 任务,我认为它基于 JSMin。

我们的想法是,让所有 js 文件保持原样(即可调试/人类可读)。当您构建 WDP 时,它将首先将 js 文件复制到 OutputPath,然后调用 CompressJavascript 目标以最小化 js 文件。这不会修改您的原始源文件,只会修改 WDP 项目的输出文件夹中的源文件。然后将文件部署在 WDP 输出路径中,其中包括预编译的站点。我在我的书(我的名字下方的链接)中介绍了这个确切的场景

您也可以让 WDP 处理创建虚拟目录,只需选中一个复选框并填写虚拟目录的名称。

关于 MSBuild 的一些链接:

赛义德·易卜拉欣·哈希米

我的书:Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

【讨论】:

  • 谢谢赛义德。只是想知道像 TeamCity 这样的 CI 工具如何能够与 WDP 交互?或者,给定一个 CI 工具,构建 msbuild .xml 文件会更好吗?谢谢
  • WDP 文件是 MSBuild 文件。因此,如果您可以在构建服务器上安装 WDP,或者您可以将它包含的文件放在源代码管理中,并根据需要手动更新路径。
  • 酷。我在上面的另一条评论中问过这个问题,但我也会问你。 “最后一个问题。我通过
  • 使用这种方法,您不必更改包含 js 文件的方式。如果它们没有被缩小,就做任何你想做的事情。如果所有的 js 文件都很小,那么将它们全部放在 site.master 中就可以了,但如果你有一些大的文件,我不会包含这些文件。实际上,这两种方法都可以,因为这些 js 文件一旦到达那里就应该缓存在客户端机器上。
【解决方案3】:

Scott Hanselman 最近写了一篇关于 combining and moving scripts to static files 的博客,基本上是使用 ScriptManagerCompositeScript 引用和 Path 属性:

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

在缩小静态文件方面,您可能必须(并且应该)在构建/部署时使用缩小工具。

【讨论】:

  • 感谢 Josef 的提醒。我将上面的一个标记为正确答案只是因为它包括压缩和最小化。
【解决方案4】:

MvcContrib.IncludeHandling 适用于这种情况。 在示例中,我有一个带有样式集合(字符串)的模型。 此外,如果我需要向页面添加自定义样式/JS,那么也可以这样做。 然后调用 Html.RenderCss 将所有样式/js 合并到一个文件中并缩小。

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

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

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

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");
%>

当它被呈现给客户端时,输出看起来像这样(缩小的组合 1 个文件)

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

API 还提供了一个调试标志,当它打开时不会缩小或组合脚本,这非常有用。

在几分钟内,我的 Yslow 分数从 F 变为 B。(24 个脚本降至 2 个)...太棒了!还有 40kbs 的下降。

明显的缺点是服务器正在动态进行压缩。我认为有一些选项可以在定义的时间段内缓存组合脚本,但这会很快缓解这种情况。

【讨论】:

【解决方案5】:

正如其他人所建议的那样,您最好创建一个静态缩小版本。或者,这里有一个可用于 .NET 的 YUICompressor 版本:http://www.codeplex.com/YUICompressor

【讨论】:

    【解决方案6】:

    您可以使用 MvcContrib.IncludeHandling。它:

    • 支持 CSS 和 JS
    • 合并到一个请求中
    • 缩小
    • Gzip/Deflate 压缩
    • 设置缓存头
    • 使用 HTMLHelper 扩展方法注册包含,然后在运行时组合它们
    • 可通过 IoC 插入

    在幕后,它使用 YUICompressor。

    【讨论】:

      【解决方案7】:

      我已经写了一些东西来自动为我处理这个问题。它使用谷歌的闭包编译器。你可以在这里阅读代码:

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

      谢谢

      圭多

      【讨论】:

        【解决方案8】:

        我知道这是一个老问题,但它首先出现在我进行一些缩小搜索时。我建议使用 Gruntjs,http://gruntjs.com。它是一个完整的网络构建工具。

        【讨论】:

          猜你喜欢
          • 2021-01-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-08-22
          • 2017-08-05
          • 2017-07-23
          • 1970-01-01
          相关资源
          最近更新 更多