46

作为这个问题的一个扩展,在用户控件中链接 JavaScript 库我正在寻找一些关于人们如何在运行中或在构建时连接和缩小 JavaScript 的示例。我还想看看它是如何在您的母版页中工作的。

我不介意页面特定的文件像当前那样被单独缩小和链接(见下文),但主母版页上的所有 JavaScript 文件(我有大约 5 或 6 个)我想连接和缩小。

任何还包含 CSS 连接和缩小的人的奖励积分!:-)

当前母版页包含我想要连接和缩小的常见 JavaScript 文件:

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

在这样的页面中使用(我很满意):

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


更新:目前的建议(2013 年末):

我会看看 Microsoft ASP.NET 内置的Bundling and Minification

4

8 回答 8

41

试试这个:

我最近在工作中完成了相当多的研究和后续开发,这对提高我们的 Web 应用程序前端的性能有很大帮助。我想我会在这里分享基本的解决方案。

显而易见的第一件事是使用 Yahoo 的 YSlow 和 Google 的 PageSpeed 对您的网站进行基准测试。这些将突出“唾手可得”的性能改进。除非您已经这样做了,否则生成的建议几乎肯定会包括合并、缩小和压缩静态内容。

我们要执行的步骤是:

编写一个自定义 HTTPHandler 来组合和缩小 CSS。编写一个自定义的 HTTPHandler 来组合和缩小 JS。包括一种机制,以确保仅在应用程序未处于调试模式时才发挥上述作用。编写自定义服务器端 Web 控件以轻松维护 css/js 文件包含。在 IIS 6 上启用某些内容类型的 GZIP。好吧,让我们从实现 .NET IHttpHandler 接口的 CSSHandler.asax 开始:

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);
        }
    }
}

好的,现在解释一下:

IsReUsable 属性:

我们不处理任何特定于实例的事情,这意味着我们可以安全地重用处理程序的同一个实例来处理多个请求,因为我们的 ProcessRequest 是线程安全的。更多信息。

处理请求方法:

这里没有什么太忙的事情。我们循环遍历提供给我们的 CSS 文件(请参阅下面的 CSSControl 了解它们是如何进入的)并使用 Yahoo 的 YUICompressor 的 .NET 端口压缩每个文件,然后将内容添加到传出响应流中。

该方法的其余部分处理设置一些 HTTP 缓存属性以进一步优化浏览器客户端下载(或不下载,视情况而定)内容的方式。

我们在代码中设置了 Etag,以便它们在我们服务器场中的所有机器上都是相同的。我们在实际文件上设置了响应和缓存依赖项,因此,如果它们被替换,缓存将失效。我们设置 Cacheability 以便代理可以缓存。我们使用我们的 cssfiles 属性 VaryByParams,以便我们可以缓存通过处理程序提交的每个 CSS 文件组。这里是 CSSControl,一个继承 .NET LiteralControl 的自定义服务器端控件。

正面:

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

后退:

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 与您的 web.config 中的以下设置挂钩:

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

因此,基本上,如果您的网站处于调试模式,您将获得如下 HTML 标记:

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

但如果您处于生产模式 (debug=false),您将获得如下标记:

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

后者显然会调用 CSSHandler,它会负责合并、缩小和缓存您的静态 CSS 内容。

然后也可以为您的静态 JavaScript 内容复制上述所有内容:

`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);
        }
    }
}

及其随附的 JSControl:

正面:

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

后退:

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

启用 GZIP:

正如 Jeff Atwood 所说,在您的网站服务器上启用 Gzip 是轻而易举的事。经过一些跟踪,我决定在以下文件类型上启用 Gzip:

.css .js .axd(Microsoft Javascript 文件) .aspx(通常的 ASP.NET Web 表单内容) .ashx(我们的处理程序) 要在 IIS 6.0 Web 服务器上启用 HTTP 压缩:

打开 IIS,右键单击网站,服务选项卡,启用压缩应用程序文件和压缩静态文件 停止 IIS 在记事本中打开 IIS 元数据库 (C:\WINDOWS\system32\inetsrv\MetaBase.xml) – 如果你“对这些事情感到紧张使用以下内容找到并覆盖两个 IIsCompressionScheme 和一个 IIsCompressionSchemes 元素:

就是这样!这为我们节省了大量带宽,并导致整个 Web 应用程序响应速度更快。

享受!

于 2009-11-11T22:44:19.247 回答
14

为什么不使用 ScriptManager?这是一个结合 AND squish的 MVCScriptManager 。

于 2009-05-26T22:07:40.637 回答
7

Professional ASP.NET 3.5的附录中, Scott Hanselman 谈到了Packer for .NET。这将与 MSBuild 集成并打包用于生产部署等的 javascript 文件。

于 2009-05-20T22:41:01.100 回答
6

使用 YUI Compressor 或 Dojo 压缩器。它们都使用 Rhino JS 解析引擎来标记您的代码,因此只有在代码是有效代码时才能工作。如果有错误,他们会通知你(这是一个很好的奖励 IMO!)另一方面,Packer 会打包你的代码,即使它包含错误。

我通过构建脚本在所有项目中使用 YUI。永远不要在飞行中进行,压缩时间太长。YUI 和 Dojo 都是基于 Java 的(ala Rhino),如果您在运行中执行此操作,您将生成后台进程来生成输出 - 不利于性能。始终在构建时进行。

于 2009-05-27T03:17:44.800 回答
4

Rejuicer 是 ASP.NET 的一个很棒的新压缩器,引起了很多关注: http ://rejuice.me

它被配置为 HTTP 模块并在运行时(一次)执行缩小并缓存输出。

它:

  • 具有流畅的配置界面
  • 允许您使用通配符规则指定要缩小的文件
  • 在 Windows Azure 上运行
  • 有点神奇地在开发环境中自动关闭,因此您可以调试原始 javascript 代码(未缩小)。

配置(在 global.asax.cs 中的 ApplicationStart 上完成)很简单:

OnRequest.ForJs("~/Combined.js")
            .Compact
            .FilesIn("~/Scripts/")
              .Matching("*.js")
            .Cache
            .Configure();
于 2011-07-06T10:48:43.293 回答
2

这是我用于连接、压缩和缓存 CSS 和 JS 文件的内容:http: //gist.github.com/130913

它只需要 bin 目录中的 Yahoo.Yui.Compressor.dll。它在编译时不会压缩,但文件会通过文件依赖项进行缓存,因此它们只加载一次,直到它们被更改。

然后我只需在 <head> 中添加此代码:

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

这在 </body> 之前:

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

它旨在处理多个文件都在同一路径中,但可以轻松升级以支持不同的路径。

于 2009-06-16T21:43:43.087 回答
2

我使用基于 MSBuild 和 Microsoft Ajax Minifier 的定制解决方案。许多现有的博客文章都不能正确处理某些情况,例如与 TFS 构建的集成。

对于每个 Web 项目,我们创建一个“wpp.targets”文件来扩展 Web 发布管道。例如,如果项目是“Website.csproj”,则在项目中创建一个名为“Website.wpp.targets”的文件。

将以下代码放在目标文件中:

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

可以根据需要修改缩小目标上的“'$(Configuration') == 'Release'”条件。在服务器上发布、打包和构建时,它会自动缩小(并验证)项目中的所有 CSS 和 JS 文件。

您可能需要为服务器构建启用 WPP“CopyWebApplication”目标。为此,请将 MSBuild 属性 UseWP_CopyWebApplication 设置为 True,并将 PipelineDependsOnBuild 设置为 False。在包含 Web 应用程序目标文件之前,我们在项目文件中设置这些。

于 2011-07-22T17:18:24.433 回答
2

I'd recommend http://www.RequestReduce.com which minimizes and combines css and javascript as well as sprites css background images and optimizes their PNG compression. It does all of this at run time and caches the output. It requires no code or configuration beyond adding the HttpModule. It serves all cached content with optimized far future headers and ETags to ensure that browsers cache the css/javascript/sprites as long as possible. While it requires no configuration, it is highly configurable and can be setup to run with a CDN and sync cached files accross a web farm.

All javascript, images and css are fetched via HTTP so it can include css and js from third parties and its also a great way to minify/combine .axd resources like WebResource.axd and ScriptResource.axd. It determines the presense of js and css via content-type so the target resource can have any (or no) extension. It runs on any IIS based technology including all versions and view engines of MVC, web forms and "web pages".

You can download from http://www.RequestReduce.com, Nuget or fork from https://github.com/mwrock/RequestReduce.

于 2011-11-10T17:35:26.730 回答