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

Cha*_*ino 46 javascript css c# asp.net-mvc minify

作为此问题的扩展,在此处链接用户控件中的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>
Run Code Online (Sandbox Code Playgroud)

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

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


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

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

Mar*_*aud 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

好的,现在解释一下:

IsReUsable属性:

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

ProcessRequest方法:

没有什么太繁忙了.在将内容添加到传出响应流之前,我们循环遍历给我们的CSS文件(参见下面的CSSControl,了解它们是如何进入的)并使用Yahoo的YUICompressor的.NET端口压缩每个文件.

该方法的其余部分涉及设置一些HTTP缓存属性以进一步优化浏览器客户端下载(或不根据情况下)内容的方式.

我们在代码中设置了Etags,以便它们可以在我们的服务器场中的所有计算机上相同.我们在实际文件上设置Response和Cache依赖关系,因此,如果替换它们,缓存将失效.我们设置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>
Run Code Online (Sandbox Code Playgroud)

背部:

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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

HttpContext.Current.IsDebuggingEnabled连接到web.config中的以下设置:

<system.web>
  <compilation debug="false">
</system.web>
Run Code Online (Sandbox Code Playgroud)

因此,基本上,如果您的站点处于调试模式,您将获得如下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>
Run Code Online (Sandbox Code Playgroud)

但是如果你处于生产模式(debug = false),你会得到这样的标记:

<link type="text/css" rel="Stylesheet" href="CssHandler.ashx?cssfiles=main.css,layout.css,formatting.css&version=1.0"/>
Run Code Online (Sandbox Code Playgroud)

后者显然会调用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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

及其随附的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>
Run Code Online (Sandbox Code Playgroud)

背部:

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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

启用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应用程序.

请享用!

  • 哇 - 这是一个令人难以置信的详细回复,肯定值得在某个地方写博客!如果它适合您的网站,绝对是一个很好的解决方案.在我的网站上,所有需要组合的js和css,无论如何都要合并,所以我真的不需要这么复杂的解决方案.是的,我启用了gzip.另外,我已经把远期未来的过期标题和我的js&css文件自动版本化到一个无cookie域 - 但这是另一个问题! (3认同)

Sco*_*man 14

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

  • 这看起来像是在飞行连接和缩小时的一个很好的选择.但我肯定会转向构建时间解决方案.没有开销的更清洁,加上我在那里可以做CSS :-) (2认同)

Red*_*ves 7

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


Bry*_*isi 6

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

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