65.9K
CodeProject 正在变化。 阅读更多。
Home

Bundling and Minification (ASP.NET 4.0)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (14投票s)

2013年12月6日

CPOL

6分钟阅读

viewsIcon

85765

downloadIcon

2230

以可管理的方式实现打包和最小化

介绍 

我知道现在讨论 ASP.NET 4.0 有点晚了,但我认为实现捆绑和最小化这个想法非常重要。我希望现在每个人都知道最小化页面资源和提高页面性能的重要性。本文不讨论捆绑和最小化为何重要,而是讨论如何在 ASP.NET 4.0 中有效地实现它。

捆绑和最小化对于网站性能至关重要。我们需要找到一种方法来使其易于管理,这样我们就无需手动进行捆绑和最小化。一旦定义,我们的代码应该会自动处理这些过程。

本文提供的代码示例已经有 1 年了(有一些修改)。我在一个项目中实现了这种管理捆绑和最小化方式,并认为应该分享这个想法。

实现捆绑和最小化的步骤

以下是使捆绑和最小化可管理的要点:

  1. 添加 Microsoft ASP.NET Web Optimization Framework:该框架用于创建文件的最小化版本以及指定的捆绑包。从 这里 下载并为您的项目添加文件引用。
  2. 创建一个 XML 文件来定义文件的捆绑。您可以为每个页面定义两个捆绑包,一个是所有页面都需要的通用文件捆绑包。第二个是页面特定文件的捆绑包。页面特定文件可能包括页面 JavaScript 和添加到页面上的所有控件的 JavaScript。
  3. 一旦在 XML 文件中定义了捆绑包,您就可以在 global.asax 页面中添加捆绑代码。此代码将负责创建文件捆绑和最小化。
  4. 将捆绑包引用添加到页面。如果调试模式为false,则只应将Bundle文件添加到页面。否则,应该将具有正常版本(非最小化)的每个单独文件添加到页面。这使得开发人员能够方便地调试 JavaScript 代码。

查看实际效果

让我们一步一步地实现所有步骤

1. 添加 Microsoft ASP.NET Web Optimization Framework

访问 https://nuget.net.cn/packages/microsoft.aspnet.web.optimization/

这将指导您如何将 System.Web.Optimization.dll 的引用添加到您的项目中。此类负责实际的捆绑和最小化。

2. 创建一个 XML 来添加捆绑定义,如下所示

<Bundling>
  <Js Name="~/AdminJS.js">
    <Path>~/scripts/jquery-1.9.0.min.js</Path>
    <Path>~/scripts/AppCommon.js</Path>
    <Path>~/scripts/jqGrid/jquery.jqGrid.min.js</Path>
    <Path>~/Scripts/jqGrid/grid.locale-en.js</Path>
  </Js>
  <Css Name="~/AdminCss.css">
    <Path>~/Styles/Site.css</Path>
  </Css>
</Bundling>  

上面的 XML 定义了主捆绑包及其子文件。同样,我们也可以用来创建 CSS 捆绑包。

您可能需要先定义 JsCssBundling 类,然后才能添加更多代码。这将允许您序列化 XML 以从 XML 文件读取捆绑文件详细信息。以下是这些类:

[Serializable]
public class Js
{
    [XmlAttribute]
    public string Name { get; set; }
    [XmlElement]
    public string[] Path { get; set; }
}
public class Css
{
    [XmlAttribute]
    public string Name { get; set; }
    [XmlElement]
    public string[] Path { get; set; }
}
[Serializable]
public class Bundling
{
    [XmlElement]
    public Js[] Js { get; set; }
    [XmlElement]
    public Css[] Css { get; set; }
} 

3. 注册捆绑信息

我们已经定义了 JavaScript 文件及其捆绑包名称。让我们看看如何读取捆绑包信息并将其添加到内存中。

public void AddBundling()
{
    string filePath = String.Empty;
    string bundleName = String.Empty;
    try
    {
        StreamReader reader = new StreamReader(HttpContext.Current.Server.MapPath
              ("~/Settings/FileBundling.xml"));

        XmlSerializer serializer = new XmlSerializer(typeof(Bundling));

        Bundling bundlingInfo = (Bundling)serializer.Deserialize(reader);

        reader.Close();
        reader.Dispose();

        #region Bundling of the css files.

        Bundle cssBundle;

        foreach (Css css in bundlingInfo.Css)
        {
            cssBundle = new Bundle(css.Name);
            foreach (string cssFile in css.Path)
            {
                cssBundle.Include(cssFile);
            }
            BundleTable.Bundles.Add(cssBundle);
        }

        #endregion

        #region Bundling of the java script files.

        Bundle jsBundle;

        foreach (Js js in bundlingInfo.Js)
        {
            bundleName = js.Name;
            jsBundle = new Bundle(js.Name);

            foreach (string jsFile in js.Path)
            {
                filePath = jsFile;
                jsBundle.Include(jsFile);
            }

            BundleTable.Bundles.Add(jsBundle);
        }

        #endregion
    }
    catch (Exception ex)
    {
        throw new Exception("There is a problem while creating Bundle", ex);
    }
} 

上面的代码将捆绑包添加到应用程序内存中。为了允许添加这些捆绑包的添加/创建,我们倾向于在 Application Start 事件中调用它们。这是代码:

void Application_Start(object sender, EventArgs e)
{
    ApplicationSettingsHelper appSettings = new ApplicationSettingsHelper();
    appSettings.AddBundling();
} 

到目前为止,我们已经创建了 XML 文件中的捆绑定义。根据捆绑定义,我们在 Application Start 事件中将捆绑包添加到内存中。

这是通用代码,允许我们将脚本文件引用添加到页面:

public static void AddScriptFromBundle(string bundleName, Page varThis, string scriptKey)
{
    StringBuilder strScript = new StringBuilder();
    if (HttpContext.Current.IsDebuggingEnabled)
    {
        StreamReader reader = new StreamReader(HttpContext.Current.Server.MapPath
                 ("~/Settings/FileBundling.xml"));
        XmlSerializer serializer = new XmlSerializer(typeof(Bundling));
        Bundling bundlingInfo = (Bundling)serializer.Deserialize(reader);
        reader.Close();
        reader.Dispose();
        foreach (Js js in bundlingInfo.Js)
        {
            if (js.Name.Trim('~') == bundleName)
            {
                foreach (string jsFile in js.Path)
                {
                    strScript.Append(String.Format("<script src=\"{0}?v=" + 
                           ConfigurationManager.AppSettings["ScriptVersion"] + 
                           "\" type=\"text/javascript\">
					</script>", jsFile.Trim('~')));
                }
                break;
            }
        }
    }
    else
    {
        strScript.Append(String.Format("<script src=\"{0}?v=" + 
          ConfigurationManager.AppSettings["ScriptVersion"] + 
				"\" type=\"text/javascript\"></script>", 
          bundleName));
    }
    varThis.ClientScript.RegisterStartupScript(varThis.GetType(), scriptKey, strScript.ToString());
} 

为什么需要这段代码来仅将一个 JavaScript 引用添加到页面?

以下几点提供了答案:

  • 我们希望在页面末尾添加脚本引用,而不是在页面中间或用户控件中。
  • 仅当调试模式为true时,才应将捆绑包添加到页面。如果调试模式为false,我们希望将每个单独的 JavaScript 文件添加到页面。这将允许我们获得干净正常的 JavaScript 文件,以便我们可以轻松调试。
  • 我们希望为添加到页面的单个文件捆绑包添加版本控制。

4. 将捆绑包引用添加到页面

现在,我们已经准备好将所有 Javascript 捆绑包添加到页面。有多种方法可以添加这些引用。每种方法都有其优点和缺点。

方法 1

向 aspx 页面添加简单的 Javascript 捆绑包引用。就像我们添加任何 javascript 引用一样,我们可以直接向页面添加捆绑文件引用。

  <script language="javascript" type="text/javascript" src="/AdminJS.js"></script>

这种方法的问题是,它不会为捆绑包添加任何版本信息。

方法 2

我们可以使用 Scripts.Render 方法向页面添加引用。此方法解决了两个问题。一是将捆绑包引用添加到页面,二是为页面添加版本信息。这些版本信息将由 Scripts.Render 本身管理。因此,每次更改 JavaScript 时,捆绑包都会随版本信息一起刷新。这将应用于回收应用程序池。

  <%:System.Web.Optimization.Scripts.Render("~/AdminJS.js")%>

方法 2 的问题是,它将在我们添加此行的页面上添加引用。首选方法是在页面底部添加文件,而不是在中间。

方法 3

以下是通过代码将 javascript 引用注入页面的方法。当您想将 Javascript 引用绑定到用户控件而不是 aspx 页面时,这是一个很大的优势。因此,如果您添加/移动用户控件,您的控件的 Javascript 文件将随用户控件一起移动。

还有一行 Page.ClientScript.IsClientScriptIncludeRegistered。这一行确保,如果您将用户控件添加到页面两次,JavaScript 将不会被添加两次,而只会添加一次。

    #region Constants

    private const string JAVA_SCRIPT_CLASS_KEY = "AdminHomePage";
    private const string CONTROL_KEY = "ControlKey";
    private const string SCRIPT_PATH = "~/AdminJS.js";

    #endregion

    protected void Page_Load(object sender, EventArgs e)
    {
        RegisterClientClass();
    }

    /// <summary>
    /// Register javascript class file. Also create a javascript class object.
    /// </summary>
    private void RegisterClientClass()
    {
        if (!Page.ClientScript.IsClientScriptIncludeRegistered(CONTROL_KEY))
        {
            Page.ClientScript.RegisterStartupScript(typeof(Page), CONTROL_KEY, Scripts.Render(SCRIPT_PATH).ToHtmlString());
        }

        Dictionary<string, string> jsControls = new Dictionary<string, string>();
        jsControls.Add(divBannerContainer.ID, divBannerContainer.ClientID);

        WebHelper.RegisterClientScriptClass(this.ClientID, this.Page, JAVA_SCRIPT_CLASS_KEY, jsControls);
    }

方法 3 将是向页面添加引用的最佳方式,但它有一个缺点。如果我向页面添加自定义 JavaScript,它应该在应用程序池回收和文件更改时具有版本。但是,像 jQuery 插件这样的文件不会随着时间的推移频繁更改。因此,我不会倾向于每次回收应用程序池时都添加新的版本信息。为了避免这个问题,我们可以添加自定义代码来从 web.config 文件维护版本信息,如以下方法所述。

方法 4

以下是 Page.ClientScript.IsClientScriptIncludeRegistered 方法的替换代码。AddScriptFromBundle 方法从 web.config 文件管理版本信息。

​  <% ApplicationSettingsHelper.AddScriptFromBundle("/AdminJS.js", this, "DefaultPage"); %> 

根据需求,选择您要实现的方法。

关注点

捆绑和最小化始终有利于页面性能。上述概念确实有助于我们免于担心 JavaScript 文件的捆绑和最小化。

此外,管理 XML 以维护页面捆绑包也很容易。

我希望这些代码和概念能帮助改进项目的框架设计。我知道总有改进和编写好代码的空间。任何评论和建议都将不胜感激。

谢谢。

© . All rights reserved.