即时或在构建时连接和缩小 JavaScript - ASP.NET MVC

IT技术 c# javascript css asp.net-mvc minify
2021-03-18 16:23:05

作为这里链接用户控件中的 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 和 Minification

6个回答

试试这个:

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

首先要做的显而易见的事情是使用雅虎的 YSlow 和谷歌的 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 缓存属性,以进一步优化浏览器客户端下载(或不下载,视情况而定)内容的方式。

我们在代码中设置 Etags,以便它们在我们服务器群中的所有机器上都相同。我们在实际文件上设置了响应和缓存依赖项,因此,如果它们被替换,缓存将失效。我们设置 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 应用程序响应速度更快。

享受!

几年后,世界已经发生了变化,尽管我需要在我的新雇主那里重新解决这个问题。放下手,我现在建议使用 Cassette:getcassette.net
2021-04-29 16:23:05
哇 - 这是一个非常详细的回复,绝对值得在某处写一篇博文!如果它适合您的网站,这绝对是一个很好的解决方案。在我的网站上,所有需要组合的 js 和 css 无论如何都组合在一起,所以我真的不需要这么复杂的解决方案。是的,我启用了 gzip。另外,我已经将远期的过期标头和我的 js 和 css 文件的自动版本控制放在一个无 cookie 的域上 - 但这是另一个问题!
2021-05-10 16:23:05

为什么不使用 ScriptManager?这是一个MVCScriptManager,它将结合 AND 挤压。

这看起来是动态连接和缩小的绝佳选择。但我绝对倾向于构建时解决方案。更干净没有开销,而且我可以在那里做 CSS :-)
2021-05-17 16:23:05

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

Packer for .NET 也执行 CSS 串联和缩小 - 查看链接:-) 但是,是的,我确实听说 YUI Compress 在缩小 JS 和 CSS 方面做得比其他任何东西都好。
2021-04-20 16:23:05
虽然这看起来不错,但 YUI Compress 似乎具有的一个优点是它也可以进行 CSS 压缩和连接。
2021-04-21 16:23:05
看起来很棒,我得试一试。我听说过关于“Packer”的坏消息,但我看到它也支持“JSMin”。
2021-05-04 16:23:05

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

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

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

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

它:

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

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

OnRequest.ForJs("~/Combined.js")
            .Compact
            .FilesIn("~/Scripts/")
              .Matching("*.js")
            .Cache
            .Configure();