目前主流浏览器限制客户端对同一域名只能同时发起6(PS:原文如此)个HTTP链接。 这意味着,打开一个网页只能同时加载6个HTTP请求,在同一个域名下其余的请求将被浏览器加入到请求队列中。 在IE浏览器中按F12调出开发人员工具,切换到网络标签,以下图所示,显示的是一个实例网站关于HTTP资源请求的状况。 javascript
灰色的进度条显示的是当前请求等待的时间,浏览器经过队列来实现其余资源的依次加载。 黄色的进度条显示的是客户端与服务器创建请求所花费的时间。 蓝色条显示的是当前资源从服务器下载完毕所花费的时间。 你能够双击当前请求查询详细状况。 例如,下图显示的是加载/ Scripts/MyScripts/JavaScript6.js文件整个请求过程的详细状况。 css
上面的图片中显示了当前资源在开始事件中被浏览器加入了请求队列。由于浏览器同时请求限制的影响,当前资源必须等待46毫秒完成上一个HTTP请求才能执行当前的请求。html
合并java
合并是ASP.NET 4.5中的新功能,使开发者很容易实现把多个文件合并成一个文件。 你能够实现CSS、javascript脚本以及其余文件的合并功能。合并多个文件意味着减小了HTTP请求的个数,同时提升了页面的加载速度。jquery
下图显示了开启了脚本合并功能后打开该网站的HTTP请求状况。web
压缩ajax
压缩功能实现了对javascript脚本和CSS进行压缩的功能,它可以去除脚本或样式中没必要要的空白和注释,同时可以优化脚本变量名的长度。 咱们来看下面这段JavaScript函数。数组
AddAltToImg = function (imageTagAndImageID, imageContext) { ///<signature> ///<summary> Adds an alt tab to the image // </summary> //<param name="imgElement" type="String">The image selector.</param> //<param name="ContextForImage" type="String">The image context.</param> ///</signature> var imageElement = $(imageTagAndImageID, imageContext); imageElement.attr('alt', imageElement.attr('id').replace(/ID/, '')); }
压缩后,该函数被合并到了一列: 浏览器
AddAltToImg = function (n, t) { var i = $(n, t); i.attr("alt", i.attr("id").replace(/ID/, "")) }
除了删除注释和多余的空格外,其变量名也被缩短为以下这样: 缓存
原变量 |
缩短后 |
imageTagAndImageID |
n |
imageContext |
t |
imageElement |
i |
合并和压缩脚本对网站性能的影响
下面表格显示了脚本被合并压缩后对网站性能提高的影响。
|
合并和压缩后 |
未压缩和合并 |
性能提高 |
HTTP请求数 |
9 |
34 |
256% |
发送字节(KB) |
3.26 |
11.92 |
266% |
响应字节(KB) |
388.51 |
530 |
36% |
耗时(ms) |
510 MS |
780 MS |
53% |
与未优化的网站相比,优化后不但减小了HTTP请求头的大小,同时请求文件的大小也有着明显的减小。 不一样文件的压缩大小是不同的,该网站的最大的脚本文件已是压缩过的(Scripts\jquery-ui-1.8.11.min.js and Scripts\jquery-1.7.1.min.js) 。
注:网站的耗时实例是经过Fiddler工具来实现的 。 (从Fiddler的Rules 菜单中选择Performance 而后选择 Simulate Modem Speeds )。
调试以及压缩Javascript
在开发环境下由于JavaScript脚本不会被压缩和合并,因此调试JavaScript是件很容易的事情(在 Web.config文件中compilation节点设置debug="true" )。你能够调试一个发布版本的JavaScript用于生产环境。 使用IE F12开发人员工具,调试JavaScript脚本的方法以下:
1 选择“脚本 ”选项卡,而后选择“开始调试”按钮。
2 选择你要调试的脚本文件。
3. 选择“配置” 按钮 ,格式化压缩后的JavaScript,而后选择“格式后的JavaScript”按钮。
4. 你还能够经过搜索方法来检索你须要调试的函数。 在下面的图片,在搜索输入框中输入AddAltToImg,搜索结果会以高亮方式显示。
关于开发人员工具调试的更多信息,请参阅MSDN文章使用开发人员工具调试JavaScript错误 。
配置脚本压缩和合并功能
在 Web.config文件中compilation节点设置debug 的值能够开启或关闭压缩和合并功能。 在下面的XML中, debug设置值为true,能够禁用脚本压缩和合并功能。
<system.web> <compilation debug="true" /> <!-- Lines removed for clarity. --> </system.web>
若是要启用脚本压缩和合并,则设置debug 为false 。你能够经过BundleTable 类的EnableOptimizations 属性来覆盖Web.config中的设置。 下面的代码演示了若是经过BundleTable 来覆盖Web.config文件中的设置:
public static void RegisterBundles(BundleCollection bundles) { bundles.Add(new ScriptBundle("~/bundles/jquery").Include( "~/Scripts/jquery-{version}.js")); // Code removed for clarity. BundleTable.EnableOptimizations = true; }
注意 :除非设置EnableOptimizations为true或设置Web.config文件中compilption 节点的debug属性为false,不然程序是不会合并和压缩文件的。 此外,系统也不会选择压缩过的脚本,而是选择调试版本。 EnableOptimizations属性的设置将会覆盖Web.config中的设置。
在ASP.NET Web窗体和Web Pages中使用脚本合并和压缩功能
在ASP.NET MVC中使用压缩和合并功能
在本节中,咱们将建立一个ASP.NET MVC项目,来体验压缩和合并功能。 首先,建立一个新的ASP.NET MVC Internet项目名为MvcBM ,其余设置默认。
打开App_Start \BundleConfig.cs文件并查找RegisterBundles方法,该方法是用来建立、注册和配置须要压缩和优化的脚本文件的。下面的代码显示了部分RegisterBundles方法。
public static void RegisterBundles(BundleCollection bundles) { bundles.Add(new ScriptBundle("~/bundles/jquery").Include( "~/Scripts/jquery-{version}.js")); // Code removed for clarity. }
上面的代码咱们建立了一个新的命名为~/bundles/jquery 的JavaScript bundle,并包含了合适的 脚本文件(用于压缩和合并,这里不包含.vsdoc),还能够经过通配符或关键字来匹配脚本文件夹下不一样版本的脚本文件。 ASP.NET MVC 4 中,系统默认集成了jquery-1.7.1.js 库。在release版本中,系统将选择jquery-1.7.1.min.js 包含在项目中。 脚本合并框架遵循如下几个原则:
{version}关键字是用来包含不一样版本的jQuery库的。 在这个例子中,使用通配符存在如下几个好处:
使用CDN
CDN jQuery 库将取代本地jQuery库。
public static void RegisterBundles(BundleCollection bundles) { //bundles.Add(new ScriptBundle("~/bundles/jquery").Include( // "~/Scripts/jquery-{version}.js")); bundles.UseCdn = true; //enable CDN support //add link to jquery on the CDN var jqueryCdnPath = "http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js"; bundles.Add(new ScriptBundle("~/bundles/jquery", jqueryCdnPath).Include( "~/Scripts/jquery-{version}.js")); // Code removed for clarity. }
在上面的代码中,在发行版中将使用CDN 上的jQuery库,而本地调试的时候使用本地版本jQuery库。 当使用CDN 版本时,你应该创建一个容错机制,预防CDN加载失败的状况。 下面的代码演示了若是在CDN 中的jQuery库加载失败的状况下如何加载本地版本。
</footer> @Scripts.Render("~/bundles/jquery") <script type="text/javascript"> if (typeof jQuery == 'undefined') { var e = document.createElement('script'); e.src = '@Url.Content("~/Scripts/jquery-1.7.1.js")'; e.type = 'text/javascript'; document.getElementsByTagName("head")[0].appendChild(e); } </script> @RenderSection("scripts", required: false) </body> </html>
建立一个Bundle
Bundle类Include方法接受一个字符串或数组类型,其中每一个字符串是一个虚拟的文件路径。其中RegisterBundles方法在App_Start /BundleConfig.cs 文件中演示了如何将多个文件添加到一个文件中:
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"));
Bundle类的IncludeDirectory方法能够添加一个目录中的全部的(和全部子目录)相匹配文件。 Bundle类IncludeDirectory API是以下所示:
public Bundle IncludeDirectory( string directoryVirtualPath, // The Virtual Path for the directory. string searchPattern) // The search pattern. public Bundle IncludeDirectory( string directoryVirtualPath, // The Virtual Path for the directory. string searchPattern, // The search pattern. bool searchSubdirectories) // true to search subdirectories.
在页面中可使用Render方法来引用建立好的包(JavaScript对应了Scripts.Render 而CSS对应的是Styles.Render )。下面的代码演示了在Views\Shared\_Layout.cshtml 文件中若是引用CSS和JavaScript的:
<!DOCTYPE html> <html lang="en"> <head> @* Markup removed for clarity.*@ @Styles.Render("~/Content/themes/base/css", "~/Content/css") @Scripts.Render("~/bundles/modernizr") </head> <body> @* Markup removed for clarity.*@ @Scripts.Render("~/bundles/jquery") @RenderSection("scripts", required: false) </body> </html>
请注意Render方法的参数是一个字符串或数组类型,它容许你在一个方法中添加多个引用。 通常状况下,你可使用Render方法来自动引用文件而省略了其余HTML标签。你可使用Url的方法来生成一个文件URL绝对路径。 假设你想使用的HTML5 异步属性。 下面的代码演示了如何使用Url方法引用Modernizr 包。
<head> @*Markup removed for clarity*@ <meta charset="utf-8" /> <title>@ViewBag.Title - MVC 4 B/M</title> <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" /> <meta name="viewport" content="width=device-width" /> @Styles.Render("~/Content/css") @* @Scripts.Render("~/bundles/modernizr")*@ <script src='@Scripts.Url("~/bundles/modernizr")' async> </script> </head>
使用通配符“*”来匹配文件
在Include 和 IncludeDirectory方法中能够经过通配符“*”来匹配路径中某一些相同元素的文件。匹配时不区分路径中的大小写。 IncludeDirectory方法具备搜索子目录的功能。
假如一个项目下有以下JavaScript文件:
下面的表演示了使用通配符来添加如图所示文件到项目中:
使用通配符 |
匹配到的文件或异常提示 |
Include("~/Scripts/Common/*.js") |
AddAltToImg.js, ToggleDiv.js, ToggleImg.js |
Include("~/Scripts/Common/T*.js") |
无效的匹配模式,通配符只能在文件的前缀或后缀中使用。 |
Include("~/Scripts/Common/*og.*") |
无效的匹配模式,一个路径中只能使用一个通配符。 |
"Include("~/Scripts/Common/T*") |
ToggleDiv.js, ToggleImg.js |
"Include("~/Scripts/Common/*") |
无效的匹配模式,仅使用一个通配符是没有任何意义的。 |
IncludeDirectory("~/Scripts/Common", "T*") |
ToggleDiv.js, ToggleImg.js |
IncludeDirectory("~/Scripts/Common", "T*",true) |
ToggleDiv.js, ToggleImg.js, ToggleLinks.js |
一般状况下,咱们应该首选经过完整路径来添加文件,其缘由以下:
bundles.Add(new StyleBundle("~/jQueryUI/themes/baseAll") .IncludeDirectory("~/Content/themes/base", "*.css"));
通配符“*.CSS”能够匹配文件夹下全部的CSS文件,包括Content\themes\base\jquery.ui.all.css 。Jquery.ui.all.css可能在其余文件中已经存在。
包缓存
合并后的系统设置了HTTP过时时间为一年。若是你重复打开了一个也没,在服务器的版本为未改变的状况下,服务器将会返回一个HTTP 304状态码,这样浏览器会加载缓存中的文件。 你也能够在IE浏览器下经过按Ctrl + F5来强制刷新页面,这时浏览器会从新请求文件,而不是从缓存中读取(此时服务器响应的HTTP状态码为200)。
下面的图像显示了在Fiddler中“Caching”选项卡缓存文件的状况 :
请求的路径为
http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81
其中AllMyScripts是优化后的包名,V = r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81是当前包的一个特定字符,用于缓存当前的文件。 只要服务器端包不改变,ASP.NET应用程序将一直使用AllMyScripts包中的该标记。 若是包中的文件存在变化,ASP.NET程序将会生成一个新的字符串,以便于刷新缓存,使客户端获得新的文件。
If you run the IE9 F12 developer tools and navigate to a previously loaded page,
IE incorrectly shows conditional GET requests made to each bundle and the server returning HTTP 304.
You can read why IE9 has problems determining if a conditional request was made
in the blog entry Using CDNs and Expires to Improve Web Site Performance. (该段不会翻译)。
在LESS, CoffeeScript, SCSS, Sass中使用脚本优化功能
合并和压缩框架提供了一个机制来处理SCSS 、Sass 、LESS和CoffeeScript并转换并合并到优化包中。 例如,添加 LESS 文件到你的MVC4项目:
using System.Web.Optimization; public class LessTransform : IBundleTransform { public void Process(BundleContext context, BundleResponse response) { response.Content = dotless.Core.Less.Parse(response.Content); response.ContentType = "text/css"; } }
var lessBundle = new Bundle("~/My/Less").IncludeDirectory("~/My", "*.less"); lessBundle.Transforms.Add(new LessTransform()); lessBundle.Transforms.Add(new CssMinify()); bundles.Add(lessBundle);
@Styles.Render("~/My/Less");
合并的注意事项
当你使用Bundle对象来建立一个包时应该用"bundle" 来作前缀,这样能够防止路由冲突 。
一旦你的包中存在一个文件更新,程序会生成一个新的查询参数来使客户端在下一次访问的时候获取到最新的版本。在传统的单独列出文件引用程序中,客户端仅会从新请求修改过的文件,可是在这种新的方式下,会从新下载整个包中的文件,所以,若是你的包常常变更,使用这种合并方式也许并非一个好的选择。
文件的合并和压缩主要是为了改善页面在第一次加载的时候文件下载所消耗的时间。当该页面加载完毕,当再一次打开该页面时,浏览器会从缓存中读取这些缓存资源(JavaScript,CSS和图像),因此,在打开同一站点的其余页面,这种方式并不能提升网站的性能(PS:由于不合并和压缩的文件也能够被缓存)。若是文件的过时设置不正确,浏览器会重复请求该文件,这种状况下合并和压缩文件会提升非第一次打开网页的性能。 有关详细信息,请参阅Using CDNs and Expires to Improve Web Site Performance 。
经过CDN能够改善浏览器同站点同时请求限制的问题。在CDN环境下,客户端将从不一样的域名来请求暑假,这个时候同一网站的资源会被缓存在不一样的主机商,从而加快网站打开速度,同时CDN还提供了数据缓存的功能。
System.Web.Optimization命名空间包含在System.Web.Optimization.DLL文件中。 它压缩功能利用的是WebGrease库(WebGrease.dll),而后使用Antlr3.Runtime.dll。
Additional Resources
· Video: Bundling and Optimizing by Howard Dierking
· Adding Web Optimization to a Web Pages Site.
· Adding Bundling and Minification to Web Forms.
· Performance Implications of Bundling and Minification on Web Browsing by Henrik F Nielsen @frystyk
· Using CDNs and Expires to Improve Web Site Performance by Rick Anderson @RickAndMSFT
· Minimize RTT (round-trip times)
Contributors
· Hao Kung
· Diana LaRose
原文地址:http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification