本文介绍 MVC 4 提供的一个新特性:捆绑(Bundle),一个在 View 和 Layout 中用于组织优化浏览器请求的 CSS 和 JavaScript 文件的技术。javascript
本文目录css
当咱们建立一个基本模板的 MVC 工程时,VS在Scripts文件夹中默认加入了一些 JavaScript 脚本库。下面是这些脚本库的简单介绍:html
另外还有一个 _references.js 文件,它的做用是经过下面这种方式放入该文件中的JS文件能够被VS智能感知:前端
/// <reference path="jquery-1.8.2.js" /> /// <reference path="jquery-ui-1.8.24.js" />
相关小技巧:在VS中让一个JS文件智能提示另外一个JS文件中的成员 。html5
在实际的项目中,咱们可能远远不止引入上面这些脚本文件,MVC 4提供的“捆绑”新功能能够很方便地对引入的脚本文件进行管理。java
选择基本模板建立一个MVC工程。和前一篇的示例差很少,建立一个名为 Appointment 的 Model,代码以下:jquery
public class Appointment { [Required] public string ClientName { get; set; } [DataType(DataType.Date)] public DateTime Date { get; set; } public bool TermsAccepted { get; set; } }
添加一个 HomeController,代码以下:web
public class HomeController : Controller { public ViewResult MakeBooking() { return View(); } [HttpPost] public JsonResult MakeBooking(Appointment appt) { return Json(appt, JsonRequestBehavior.AllowGet); } }
给MakeBooking action添加一个 MakeBooking.cshtml 视图,代码以下:ajax
@model MvcApplication1.Models.Appointment @{ AjaxOptions ajaxOpts = new AjaxOptions { OnSuccess = "processResponse" }; } <h4>Book an Appointment</h4> <script src="~/Scripts/Home/MakeBooking.js"></script> <div id="formDiv" class="visible"> @using (Ajax.BeginForm(ajaxOpts)) { @Html.ValidationSummary(true) <p>@Html.ValidationMessageFor(m => m.ClientName)</p> <p>Your name: @Html.EditorFor(m => m.ClientName)</p> <p>@Html.ValidationMessageFor(m => m.Date)</p> <p>Appointment Date: @Html.EditorFor(m => m.Date)</p> <p>@Html.ValidationMessageFor(m => m.TermsAccepted)</p> <p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms & conditions</p> <input type="submit" value="Make Booking" /> } </div> <div id="successDiv" class="hidden"> <h4>Your appointment is confirmed</h4> <p>Your name is: <b id="successClientName"></b></p> <p>The date of your appointment is: <b id="successDate"></b></p> <button id="backButton">Back</button> </div>
把该视图须要的 JavaScript 代码放在一个单独的文件 MakeBooking.js 中,并将该JS文件放在 /Scripts/Home 文件夹下,该JS文件代码以下:浏览器
function processResponse(appt) { $('#successClientName').text(appt.ClientName); $('#successDate').text(processDate(appt.Date)); switchViews(); } function processDate(dateString) { var date = new Date(parseInt(dateString.substr(6, dateString.length - 8))); return date.toLocaleDateString(); } function switchViews() { var hidden = $('.hidden'); var visible = $('.visible'); hidden.removeClass("hidden").addClass("visible"); visible.removeClass("visible").addClass("hidden"); } $(document).ready(function () { $('#backButton').click(function (e) { switchViews(); }); });
注意,这里须要对后台经过Json方法返回的日期进行处理,Json 方法返回的日期格式是:/Date(1385308800000)/,因此把它呈现给客户端时用 processDate JS方法处理了一下。
在 Content 文件夹下添加一个样式文件 CustomStyles.css,代码以下:
div.hidden { display: none;} div.visible { display: block;}
最后清理一下 _Layout.cshtml 文件,以下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> </head> <body> @RenderBody() </body> </html>
本文将关心的是 JS 和 CSS 文件的引用,你们不用关心代码自己,此时只需Copy好代码,一会使用捆绑让它运行起来。
之前咱们引入脚本和样式文件的时候,都是一个个的引用,看起来一大坨,不当心还会弄错前后次序,管理非常不便。并且不少脚本库有普通和 min 两个版本,开发的时候咱们引入普通版本以方便调试,发布的时候又换成min版本以减小网络带宽,非常麻烦。为此,MVC 4 增长了一个新功能:“捆绑”,它的做用是把一类脚本或样式文件捆绑在一块儿,在须要用的时候调用一句代码就行,极大地方便了脚本和样式文件的管理;并且能够把脚本的普通和 min 两个版本都捆绑起来,MVC也会根据是否为Debug模式智能地选择脚本文件的版本。下面咱们来看看这个捆绑功能的使用。
用捆绑方便之一是能够在 /App_Start/BundleConfig.cs 中经过注册来统一管理脚本和样式文件。咱们能够打开 BundleConfig.cs 文件看看VS 已经默认对脚本和样式文件的捆绑状况。为了演示如何使用捆绑,咱们把VS默认的捆绑代码删除,再增长咱们须要的捆绑,以下所示:
public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/*.css")); bundles.Add(new ScriptBundle("~/bundles/clientfeaturesscripts").Include( "~/Scripts/jquery-{version}.js", "~/Scripts/jquery.validate.js", "~/Scripts/jquery.validate.unobtrusive.js", "~/Scripts/jquery.unobtrusive-ajax.js")); } }
捆绑是经过 RegisterBundles 参数对象的 Add 方法添加。Add 方法的参数须要一个ScriptBundle 类 或 StyleBundle 类的实例对象, 脚本文件用的是 ScriptBundle 类,样式文件用的是 StyleBundle 类,它们的构造参数表明着捆绑在一块儿的文件的引用。 Include 方法用于包含具体要捆绑的文件。其中的 {version} 是文件版本的占位符,MVC会在相应的目录下定位到最新的一个版本文件。
使用捆绑方便之二是不再用引入一大坨的文件了,以下面的 _Layout.cshtml:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> @Styles.Render("~/Content/css") </head> <body> @Scripts.Render("~/bundles/clientfeaturesscripts") @RenderBody() </body> </html>
这里经过 @Scripts.Render 和 @Styles.Render 两个Helper方法添加捆绑。须要注意的是,MakeBooking.cshtml 文件引入的 MakeBooking.js 是基于 jQuery的,因此 _Layout.cshtml的 @Scripts.Render("~/bundles/clientfeaturesscripts") 必须放在 @RenderBody() 以前。
上面咱们提到在 _Layout.cshtml 中,@Scripts.Render("~/bundles/clientfeaturesscripts") 必须放在 @RenderBody() 以前,由于View引入的JS是基本jQuery的。有时候因为某种需求或我的的喜爱要把 @RenderBody() 放在 @Scripts.Render("~/bundles/clientfeaturesscripts") 以前,若是这样的话,MakeBooking.js 文件就在jQuery库以前引用了,显然JS会报错。
对于这种状况,咱们能够用 [ASP.NET MVC 小牛之路]12 - Section、Partial View 和 Child Action 文章介绍的Section来解决这种JS引用次序的问题。以下,咱们能够把 MakeBooking.cshtml 中引入JS的部分用section包起来:
... <h4>Book an Appointment</h4> @section scripts { <script src="~/Scripts/Home/MakeBooking.js" type="text/javascript"></script> } ...
而后咱们就能够在 _Layout.cshtml 全部Render方法后面使用 @RenderSection("scripts", required: false) 方法引入MakeBooking.js 文件,这样就不用关心在 _Layout.cshtml 中的 @RenderBody() 和 @Scripts.Render("~/bundles/clientfeaturesscripts") 的前后次序了。以下所示:
<body> @RenderBody() @Scripts.Render("~/bundles/clientfeaturesscripts") @RenderSection("scripts", required: false) </body>
这样保证了 MakeBooking.js 必定在jQuery库文件以后引用。
捆绑除了能够方便地管理脚本和样式文件,还能够给网络减小带宽。
如下是在Debug模式下使用捆绑MVC生成引用部分的代码:
<link href="/Content/CustomStyles.css" rel="stylesheet"/> <link href="/Content/Site.css" rel="stylesheet"/> ... <script src="/Scripts/jquery-1.8.2.js"></script> <script src="/Scripts/jquery.unobtrusive-ajax.js"></script> <script src="/Scripts/jquery.validate.js"></script> <script src="/Scripts/jquery.validate.unobtrusive.js"></script> ... <script src="/Scripts/Home/MakeBooking.js"></script>
总共 7 个文件,在Debug模式下使用捆绑和不使用捆绑没什么区别。
下面咱们来比较一下在发布模式下不使用捆绑和使用捆绑二者使用带宽的状况。
在 Web.config 中把调式模式关闭,以下:
... <system.web> <httpRuntime targetFramework="4.5" /> <compilation debug="false" targetFramework="4.5" /> ...
咱们先来看看不使用捆绑使用带宽的状况,为此,咱们 MakeBooking.cshtml 中的section部分 和 _Layout.cshtml 中的引用捆绑的部分注释掉。并在 _Layout.cshtml 中引入上面 7 个文件,以下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> @*@Styles.Render("~/Content/css")*@ <link href="~/Content/CustomStyles.css" rel="stylesheet"/> <link href="~/Content/Site.css" rel="stylesheet" /> <script src="~/Scripts/jquery-1.8.2.js"></script> <script src="~/Scripts/jquery.unobtrusive-ajax.js"></script> <script src="~/Scripts/jquery.validate.js"></script> <script src="~/Scripts/jquery.validate.unobtrusive.js"></script> <script src="~/Scripts/Home/MakeBooking.js"></script> </head> <body> @RenderBody() @*@Scripts.Render("~/bundles/clientfeaturesscripts") @RenderSection("scripts", required: false)*@ </body> </html>
运行程序,用IE F12 工具查看请求服务器资源的状况(注意要先清理缓存),结果以下:
咱们看到在不使用捆绑的状况下,客户端接收的数据总大小为330.65 KB。
咱们再来看看使用捆绑带宽的使用状况。咱们把以前在 MakeBooking.cshtml 和 _Layout.cshtml 中的注释去掉,并把 _Layout.cshtml 引入 7 个文件的代码删除。
运行程序(注意清涂缓存),结果以下:
咱们看到,使用捆绑客户端接收的数据总大小为 124.01 KB,和不使用捆绑相比少200多KB,即一半多,这是很是可观的。
咱们也注意到,使用捆绑请求的连接也少了。这是由于在发布模式下,响应客户端请求时,MVC整合并最小化了JavaScript文件和样式文件,并使得一个捆绑中的内容在一个请求中加载。若是咱们查看Html代码,能够看到 Styles.Render 和 Scripts.Render 两个方法生成了这样的HTML引用代码:
<link href="/Content/css?v=6jdfBoUlZKSHjUZCe_rkkh4S8jotNCGFD09DYm7kBWE1" rel="stylesheet"/> ... <script src="/bundles/clientfeaturesscripts?v=KyclumLmAXQGM1-wDTwVUS31lpYigmXXR8HfERBGk_I1"></script> .. <script src="/Scripts/Home/MakeBooking.js"></script>
一个Styles.Render 或 Scripts.Render 方法生成了一个带有v参数的URL,这个URL将使MVC把一整个捆绑的数据进行最小化处理并一次发送到客户端。
参考:《Pro ASP.NET MVC 4 4th Edition》