我怎样才能在ASP.NET MVC应用程序中自动压缩和最小化JavaScript文件?

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>

谢谢


点击这个问题查看更多/其他选项:https://dev59.com/KXNA5IYBdhLWcg3wn_U1 - Charlino
8个回答

24
我个人认为,在开发过程中保持文件的分离是非常有价值的,而在生产环境中才更加重要。因此,我修改了我的部署脚本,以实现以上目标。
我有一个部分的内容如下:
<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>
<% } %>

构建脚本将所有在“section”中的文件合并在一起。然后我使用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. 你的自动化部署流程是什么?我仍然在老派的方式下进行,只是进行发布构建,然后右键单击项目并选择“发布”。所以我不确定如何设置它,也不知道你的部署脚本如何适用。你能详细介绍一下吗?谢谢。
- wgpubs
哎呀...哈哈,这是一个扩展方法。它基本上与Url.Content相同,只是我在末尾添加了http修订号。修订号应更新为SVN修订号,以保持唯一性。 - Min
尽管我不确定从哪里开始使用MSBuild,但我会将其标记为正确答案。这些msbuild脚本是放在单独的项目中还是只是闲置在某个独立的位置?有没有推荐的教程/入门指南可以学习MSBuild?再次感谢! - wgpubs
我只是把MSBuild脚本放在一个单独的文件中。实际上,我并没有记得读过任何有关MS Build的单一教程。我通过阅读大量的MSDN文档来理解它。我建议先从概述开始。http://msdn.microsoft.com/en-us/library/ms171452.aspx - Min
我建议使用 ViewContext.HttpContext 而不是 HttpContext.Current - Anthony Serdyukov
显示剩余3条评论

8
实际上,有一种更简单的方法,使用Web Deployment Projects(WDP)。 WDP将管理 aspnet__compiler aspnet__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的一些链接:

Sayed Ibrahim Hashimi

我的书:深入了解 Microsoft Build 引擎:使用 MSBuild 和 Team Foundation Build


谢谢Sayed。我在想像TeamCity这样的CI工具如何与WDP进行交互?或者,如果有一个CI工具,是不是最好使用构建msbuild .xml文件的方式?谢谢。 - wgpubs
WDP文件是MSBuild文件。因此,如果您可以在构建服务器上安装WDP或将其包含的文件放置在源代码控制中,并手动更新路径(如果需要)。 - Sayed Ibrahim Hashimi
很好。我在另一条评论中问过这个问题,但我也会问你。 “最后一个问题。我通过< script>标签在我的site.master和许多视图中引用javascript文件。这意味着某些视图将包含比其他视图更多的javascript文件。鉴于此,什么是最好的?我应该只在site.master中包含所有javascript文件并遵循您上面的程序-还是我应该简化所有单独的.js文件而不担心将它们合并成一个单独的.js文件?谢谢。”你有什么想法?非常感谢。好东西。 - wgpubs
采用这种方法,您无需更改包含js文件的方式。只需像没有缩小文件一样做即可。如果所有js文件都很小,则将它们全部放在site.master中就可以了,但是如果有一些大文件,我不会包括它们。实际上,任何一种方法都可以,因为这些js文件一旦到达客户端机器,就应该被缓存。 - Sayed Ibrahim Hashimi

4

Scott Hanselman最近在博客中谈到了将脚本合并并移动到静态文件中,基本上是使用ScriptManagerCompositeScript引用和Path属性:

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

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

感谢约瑟夫的提醒。我将上面的答案标记为正确答案,因为它包含了压缩和最小化两个方面。谢谢! - wgpubs

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

当这个被呈现给客户端时,输出的结果看起来就像这样(最小化合并一个文件)。
<link rel='stylesheet' type='text/css' href='/include/css/-QdUg9EnX5mpI0e4aKAaOySIbno%40'/>

API还提供了一个debug标志,当设置时不会最小化或组合脚本,这非常有用。
在几分钟内,我的Yslow分数从F提高到B。(24个脚本减少到2个)...太棒了!并且减少了40kbs。
显然的缺点是服务器正在即时压缩。我认为有选项可以缓存组合脚本一段时间,这将快速缓解这个问题。

关于缓存的问题,是有的。在每种包含类型中都有一个名为“cacheFor = {timespan}”的配置选项 - 请参见http://github.com/mvccontrib/MvcContrib/blob/master/src/MvcContrib.IncludeHandling/Configuration/IIncludeTypeSettings.cs以及http://github.com/mvccontrib/MvcContrib/blob/master/src/MVCContrib.UnitTests/IncludeHandling/configs/CanChangeAllTheDefaultsEvenThoughIShouldntWriteATestWithABigSurfaceAreaLikeThisNaughtyPete.xml。它默认设置为一年。很高兴听到您喜欢它 :-) - Peter Mounce
另外请注意,Html.IncludeCss有一个params重载,以及一个IEnumerable<string>包含重载,所以您应该能够在您的示例中执行Html.IncludeCss(Model.Styles)。 - Peter Mounce

2
您可以使用MvcContrib.IncludeHandling。它可以:
  • 支持CSS和JS
  • 将它们合并到一个请求中
  • 压缩文件
  • 使用Gzip/Deflate进行压缩
  • 设置缓存头
  • 使用HTMLHelper扩展方法在运行时注册包含内容,然后将它们组合起来
  • 可通过IoC进行插件化
在底层,它使用YUICompressor。

2

0

0

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接