本文介紹 MVC 4 提供的一個新特性:捆綁(Bundle),一個在 View 和 Layout 中用於組織優化瀏覽器請求的 CSS 和 JavaScript 文件的技術。
本文目錄
了解VS默認加入的腳本庫
當我們創建一個基本模板的 MVC 工程時,VS在Scripts文件夾中默認加入了一些 JavaScript 腳本庫。下面是這些腳本庫的簡單介紹:
- jquery-1.8.2.js,這個就不用解釋了。
- jquery-ui-1.8.24.js,在jQuery 基礎上的一套界面工具,包括了網頁上常見的很多插件和動畫特效。
- jquery.validate.js,用於驗證用戶在表單內input元素輸入的數據。
- knockout-2.2.0.js,是一個輕量級的UI類庫,通過應用MVVM模式使JavaScript前端UI簡單化,更多:http://knockoutjs.com/documentation/introduction.html。
- modernizr-2.6.2.js,一個開源的JS庫,它使得那些基於訪客瀏覽器的不同(指對新標准支持性的差異)而開發不同級別體驗的設計師的工作變得更為簡單。它使得設計師可以在支持HTML5和CSS3的瀏覽器中充分利用HTML5和CSS3的特性進行開發,同時又不會犧牲其他不支持這些新技術的瀏覽器的控制。更多:http://www.mhtml5.com/2011/03/676.html 。
- jquery.unobtrusive-ajax.js,MVC 框架中使用 Unobtrusive Ajax 的庫,更多:[ASP.NET MVC 小牛之路]14 - Unobtrusive Ajax 。
- jquery.validate.unobtrusive.js,基於 jquery.unobtrusive-ajax.js,更多:[ASP.NET MVC 小牛之路]15 - Model Binding 。
另外還有一個 _references.js 文件,它的作用是通過下面這種方式放入該文件中的JS文件可以被VS智能感知:
/// <reference path="jquery-1.8.2.js" /> /// <reference path="jquery-ui-1.8.24.js" />
相關小技巧:在VS中讓一個JS文件智能提示另一個JS文件中的成員 。
在實際的項目中,我們可能遠遠不止引入上面這些腳本文件,MVC 4提供的“捆綁”新功能可以很方便地對引入的腳本文件進行管理。
准備工作
選擇基本模板創建一個MVC工程。和前一篇的示例差不多,創建一個名為 Appointment 的 Model,代碼如下:

public class Appointment { [Required] public string ClientName { get; set; } [DataType(DataType.Date)] public DateTime Date { get; set; } public bool TermsAccepted { get; set; } }
添加一個 HomeController,代碼如下:

public class HomeController : Controller { public ViewResult MakeBooking() { return View(); } [HttpPost] public JsonResult MakeBooking(Appointment appt) { return Json(appt, JsonRequestBehavior.AllowGet); } }
給MakeBooking action添加一個 MakeBooking.cshtml 視圖,代碼如下:
@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() 之前。
使用腳本 Section
上面我們提到在 _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》