ASP.NET MVC 4 RC的JS/CSS打包压缩功能

打包 (Bundling) 及压缩 (Minification) 指的是将多个 js 文件或 css 文件打包成单一文件并压缩的做法,如此可减少浏览器需下载多个文件案才能完成网页显示的延迟感,同时通过移除 JS/CSS 文件案中空白、批注及修改 JavaScript 内部函数、变量名称的压缩手法,能有效缩小文件案体积,提高传输效率,提供使用者更流畅的浏览体验。

在 ASP.NET MVC 4 中可以使用 BundleTable 捆绑多个 css 文件和 js 文件,以提高网络加载速度和页面解析速度。更为重要的是通过捆绑可以解决 IE 浏览器的 31 个 CSS 文件连接的限制。在做 ASP.Net 项目时很多时候会使用一些开源的 javascript 控件。无形中增加了 css 和 javascript 文件的引用。如果手工将这些 css 文件合并将给将来版本升级造成很大的麻烦。于是,我们只好小心翼翼的处理这些 css 文件在页面中的引用。ASP.NET 捆绑是 ASP.NET 4.5 的新功能,是 System.Web.Optimization 命名空间下。他提供了一些 ASP.NET 运行性能方面的优化,比如,一个页面可能有很多 CSS/JS/ 图片, 通过灵活的应用 BundleTable 类,他可以帮你将文件合并压缩代码优化成一个最理想的文件,然后输出到客户端,从而提高了浏览器下载速度。

现在我们终于有一个以相对比较完美的解决方案来解决 css 文件和 javascirpt 文件给我们带来的麻烦,BundleTable 捆绑技术很好的解决了这个问题。

在 ASP.NET MVC 4 Beta 时代便已内建打包压缩功能,做法是在 global.asax.cs 的 Application_Start 加入

BundleTable.Bundles.EnableDefaultBundles();

如此,便可使用以下写法一口气将整个 Scripts 目录下的 JS 及 Contents 目录下所有 CSS 打包并压缩成单一文件案,改善网页载入效率: (参考)

<script src="@Url.Content("~/Scripts/js")" type="text/javascript"></script>
<script src="@Url.Content("~/Content/CSS")" type="text/javascript"></script>

最近在看 ASP.NET MVC 4 RC,发现 RC 版在打包压缩做法上又有所革新,变得更加弹性有条理。

原本打包规则被藏在 global.asax.cs Application_Start 中,RC 版起则多了一个新目录 App_Start,其中包含 RouteConfig.cs、FilterConfig.cs、BundleConfig.cs 三个类,做法上改为通过 WebActivator 启动。新的系统配置将路由规则、过滤器及打包规则等注册逻辑由 Application_Start 中拆出来,各自放在独立文件案中,管理及修改起来一目了然,架构上更漂亮。

BundleConfig.cs 的预设内容如下:

using System.Web;

using System.Web.Optimization;

namespace MyMvcApplicaiton

{

public class BundleConfig

{

public static void RegisterBundles(BundleCollection bundles)

{

bundles.Add(new ScriptBundle("~/bundles/jquery").Include(

"~/Scripts/jquery-1.*"));

bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(

"~/Scripts/jquery-ui*"));

bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(

"~/Scripts/jquery.unobtrusive*",

"~/Scripts/jquery.validate*"));

bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(

"~/Scripts/modernizr-*"));

bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"));

bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(

"~/Content/themes/base/jquery.ui.core.css",

"~/Content/themes/base/jquery.ui.resizable.css",

"~/Content/themes/base/jquery.ui.selectable.css",

"~/Content/themes/base/jquery.ui.accordion.css",

"~/Content/themes/base/jquery.ui.autocomplete.css",

"~/Content/themes/base/jquery.ui.button.css",

"~/Content/themes/base/jquery.ui.dialog.css",

"~/Content/themes/base/jquery.ui.slider.css",

"~/Content/themes/base/jquery.ui.tabs.css",

"~/Content/themes/base/jquery.ui.datepicker.css",

"~/Content/themes/base/jquery.ui.progressbar.css",

"~/Content/themes/base/jquery.ui.theme.css"));

}

}

}

跟 Beta 时代很大的差异是将 JS 与 CSS 加以群组化,分别定义出 jquery, jqueryui, jqueryval, modernizr, css 及 themes/base/css 等群组,让网页可以视需要只加载必要的 JS 及 CSS 文件群组,不像先前每次得打包整个目录,对于 JS 文件的加载顺序及相依性也能做较精准的调控。

而在.cshtml 中,则使用 Styles.Render 及 Scripts.Render 载入 BundleConfig.cs 所定义的 JS 及 CSS 群组,例如:

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8" />

<meta name="viewport" content="width=device-width" />

<title>@ViewBag.Title</title>

@Styles.Render("~/Content/themes/base/css", "~/Content/css")

@Scripts.Render("~/bundles/modernizr")

@Scripts.Render("~/bundles/jquery", "~/bundles/jqueryui",

"~/bundles/jqueryval")

@RenderSection("scripts", required: false)

</head>

<body>

@RenderBody()

</body>

</html>

接着来实测一下,做一个简单的 Index.cshtml,中间只有 <div>Hello</div> 一行,配合上述的 _Layout.cshtml,进行测试,没想到呈现的源代码如下,一个个 CSS 及 JS 文件都是分开的,没打包也没压缩?

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8" />

<meta name="viewport" content="width=device-width" />

<title></title>

<link href="/Content/themes/base/jquery.ui.core.css" rel="stylesheet" type="text/css" />

<link href="/Content/themes/base/jquery.ui.resizable.css" rel="stylesheet" type="text/css" />

<link href="/Content/themes/base/jquery.ui.selectable.css" rel="stylesheet" type="text/css" />

<link href="/Content/themes/base/jquery.ui.accordion.css" rel="stylesheet" type="text/css" />

<link href="/Content/themes/base/jquery.ui.autocomplete.css" rel="stylesheet" type="text/css" />

<link href="/Content/themes/base/jquery.ui.button.css" rel="stylesheet" type="text/css" />

<link href="/Content/themes/base/jquery.ui.dialog.css" rel="stylesheet" type="text/css" />

<link href="/Content/themes/base/jquery.ui.slider.css" rel="stylesheet" type="text/css" />

<link href="/Content/themes/base/jquery.ui.tabs.css" rel="stylesheet" type="text/css" />

<link href="/Content/themes/base/jquery.ui.datepicker.css" rel="stylesheet" type="text/css" />

<link href="/Content/themes/base/jquery.ui.progressbar.css" rel="stylesheet" type="text/css" />

<link href="/Content/themes/base/jquery.ui.theme.css" rel="stylesheet" type="text/css" />

<link href="/Content/site.css" rel="stylesheet" type="text/css" />

<script src="/Scripts/modernizr-2.0.6-development-only.js" type="text/javascript"></script>

<script src="/Scripts/jquery-1.6.2.js" type="text/javascript"></script>

<script src="/Scripts/jquery-ui-1.8.11.js" type="text/javascript"></script>

<script src="/Scripts/jquery.unobtrusive-ajax.js" type="text/javascript"></script>

<script src="/Scripts/jquery.validate.js" type="text/javascript"></script>

<script src="/Scripts/jquery.validate.unobtrusive.js" type="text/javascript"></script>

</head>

<body>

<div>Hello</div>

</body>

</html>

原来,这也是 ScriptBundle 及 StyleBundle 的贴心之处,在调试模式下,会展现 CSS 及 JS 原貌,方便开发人员检查源代码找问题与除错。要见识它的打包压缩效果,记得要设定 <compilation debug="false" targetFramework="4.0" />。

关闭调试模式后,网页的源代码就变成以下的样子,一个群组只有一个 <link> 或 <script>,而 href 及 src 会指向 /Content/css?v=ji3nXsakWko…(包含哈希码参数,以确保文件案变动时只会载入新版) 格式的连结,传回多个文件案打包及压缩后的内容:

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8" />

<meta name="viewport" content="width=device-width" />

<title></title>

<link href="/Content/themes/base/css?v=UM62... 略" rel="stylesheet" type="text/css" />

<link href="/Content/css?v=ji3n... 略" rel="stylesheet" type="text/css" />

<script src="/bundles/modernizr?v=XGaE... 略" type="text/javascript"></script>

<script src="/bundles/jquery?v=3AwA... 略" type="text/javascript"></script>

<script src="/bundles/jqueryui?v=bMdf... 略" type="text/javascript"></script>

<script src="/bundles/jqueryval?v=uFE7... 略" type="text/javascript"></script>

</head>

<body>

<div>Hello</div>

</body>

</html>

最后,实际测量二者的性能差别:

clip_image001
在未打包压缩前,加载网页需要发出 20 个请求,总共传输 5,992+812,541=818,533 Bytes 的资料。

clip_image002
打包压缩后,请求数下降到 7 个,数据传输量也减少为 2,274+352,454=354,728 Bytes,数据传输量只有原本的 43.33%!

在开发 ASP.NET MVC 4 项目时,不要忘记这个有用的机制。

最后介绍一个System.Web.Optimization的扩展库 http://bundletransformer.codeplex.com/,推荐在 ASP.NET MVC 4 项目中使用。

http://www.codeproject.com/Articles/395143/JavascriptHelper-Managing-JS-files-for-ASP-NET-MVC

[翻译]ASP.NET MVC4 新特性之脚本压缩和合并