本文介绍 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; }
}

Appointment

添加一个 HomeController,代码如下:

public class HomeController : Controller {
public ViewResult MakeBooking() {
return View();
} [HttpPost]
public JsonResult MakeBooking(Appointment appt) {
return Json(appt, JsonRequestBehavior.AllowGet);
}
}

HomeController

给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();
});
});

MakeBooking.js

注意,这里需要对后台通过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>

_Layout.cshtml

本文将关心的是 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》

[ASP.NET MVC 小牛之路]17 - 捆绑(Bundle)的更多相关文章

  1. [ASP.Net] 转 > ASP.NET MVC 小牛之路

    URL: http://www.cnblogs.com/willick/ 看到了不错的学习笔记,MVC.Net学习之路展开   [ASP.NET MVC 小牛之路]18 - Web API [ASP. ...

  2. [ASP.NET MVC 小牛之路]16 - Model 验证

    上一篇博文 [ASP.NET MVC 小牛之路]15 - Model Binding 中讲了MVC在Model Binding过程中如何根据用户提交HTTP请求数据创建Model对象.在实际的项目中, ...

  3. [ASP.NET MVC 小牛之路]04 - 依赖注入(DI)和Ninject

    本人博客已转移至:http://www.exblr.com/liam  为什么需要依赖注入 在[ASP.NET MVC 小牛之路]系列的理解MVC模式文章中,我们提到MVC的一个重要特征是关注点分离( ...

  4. [ASP.NET MVC 小牛之路]05 - 使用 Ninject

    在[ASP.NET MVC 小牛之路]系列上一篇文章(依赖注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的两件事情,续这篇文章之后,本文将用一个实际的示例来 ...

  5. [ASP.NET MVC 小牛之路]06 - 使用 Entity Framework

    在家闲着也是闲着,继续写我的[ASP.NET MVC 小牛之路]系列吧.在该系列的上一篇博文中,在显示书本信息列表的时候,我们是在程序代码中手工造的数据.本文将演示如何在ASP.NET MVC中使用E ...

  6. [ASP.NET MVC 小牛之路]10 - Controller 和 Action (2)

    继上一篇文章之后,本文将介绍 Controller 和 Action 的一些较高级特性,包括 Controller Factory.Action Invoker 和异步 Controller 等内容. ...

  7. [ASP.NET MVC 小牛之路]13 - Helper Method

    我们平时编程写一些辅助类的时候习惯用“XxxHelper”来命名.同样,在 MVC 中用于生成 Html 元素的辅助类是 System.Web.Mvc 命名空间下的 HtmlHelper,习惯上我们把 ...

  8. [ASP.NET MVC 小牛之路]15 - Model Binding

    Model Binding(模型绑定)是 MVC 框架根据 HTTP 请求数据创建 .NET 对象的一个过程.我们之前所有示例中传递给 Action 方法参数的对象都是在 Model Binding ...

  9. [ASP.NET MVC 小牛之路]18 - Web API

    Web API 是ASP.NET平台新加的一个特性,它可以简单快速地创建Web服务为HTTP客户端提供API.Web API 使用的基础库是和一般的MVC框架一样的,但Web API并不是MVC框架的 ...

随机推荐

  1. Python-模块的使用

    基础概念 - 模块是Python组织代码的基本方式 - 一个Python脚本可以单独运行,也可以导入到另一个脚本中运行,当脚本被导入运行时,我们将其称为模块(module) - 所有的.py文件都可以 ...

  2. qtranslate

    qtranslate可以帮助用户快速的建立多语言网站平台,qTranslate 插件就是一个功能强大的 WordPress 多语言插件. 它允许用户在配置页添加新的语言.在 URL 结构方面,qTra ...

  3. JS—事件对象

    在触发DOM上的某个事件时,会产生一个事件对象event.这个对象中包含着所有与事件有关的信息.包括导致事件的元素,事件的类型以及其他与特定事件相关的信息. 举例鼠标操作导致的事件对象中,会包含鼠标位 ...

  4. java中被各种XXUtil/XXUtils辅助类恶心到了,推荐这种命名方法

    且看一下有多少个StringUtils 列举一下XXUtil/XXUtils恶劣之处 1. 不知道该用XXUtil还是用XXUtils, 或者XXHelper, XXTool 2. 不知道该用a.ja ...

  5. nodejs复习02

    process 这个模块是单线程的,无法完全利用多核CPU 基本信息 //程序目录 process.cwd(); //应用程序当前目录 process.chdir('/home'); //改变应用程序 ...

  6. 利用DNS Zone Transfers漏洞工具dnswalk

    利用DNS Zone Transfers漏洞工具dnswalk   DNS Zone Transfers(DNS区域传输)是指一台备用服务器使用来自主服务器的数据刷新自己的域(zone)数据库.当主服 ...

  7. Effective C++ 笔记1

    条款1:视C++为一个语言联邦 1.C.Object-Oriented C++.Template C++ .STL 组成了C++,高效编程取决你使用C++的哪一部分 条款2:尽量用const ,enu ...

  8. @WebServlet用注解来实现servlet和url的映射

    package com.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.Se ...

  9. requirejs 打包 添加版本号收集资料 待测试

    https://www.npmjs.org/package/rjs-optimhttps://www.npmjs.org/package/grunt-requirejs-md5指定js版本号 但不是M ...

  10. [译]App Framework 2.1 (1)之 Quickstart

    最近有移动App项目,选择了 Hybrid 的框架Cordova  和  App Framework 框架开发. 本来应该从配置循序渐进开始写的,但由于上班时间太忙,这段时间抽不出空来,只能根据心情和 ...