本文介紹 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》