1. 程式人生 > net教程 > [翻譯]ASP.NET MVC4新特性之指令碼壓縮和合並

[翻譯]ASP.NET MVC4新特性之指令碼壓縮和合並

目前主流瀏覽器限制客戶端對同一域名只能同時發起6(PS:原文如此)個HTTP連線。 這意味著,開啟一個網頁只能同時載入6個HTTP請求,在同一個域名下其他的請求將被瀏覽器加入到請求佇列中。 在IE瀏覽器中按F12調出開發人員工具,切換到網路標籤,如下圖所示,顯示的是一個例項網站關於HTTP資源請求的情況。 

 灰色的進度條顯示的是當前請求等待的時間,瀏覽器通過佇列來實現其他資源的依次載入。 黃色的進度條顯示的是客戶端與伺服器建立請求所花費的時間。 藍色條顯示的是當前資源從伺服器下載完畢所花費的時間。 你可以雙擊當前請求查詢詳細情況。 例如,下圖顯示的是載入/ Scripts/MyScripts/JavaScript6.js檔案整個請求過程的詳細情況。 

 [翻譯]ASP.NET MVC4新特性之指令碼壓縮和合並_ASP.NET

 上面的圖片中顯示了當前資源在開始事件中被瀏覽器加入了請求佇列。因為瀏覽器同時請求限制的影響,當前資源必須等待46毫秒完成上一個HTTP請求才能執行當前的請求。

合併

合併是ASP.NET 4.5中的新功能,使開發者很容易實現把多個檔案合併成一個檔案。 你可以實現CSS、javascript指令碼以及其他檔案的合併功能。合併多個檔案意味著減少了HTTP請求的個數,同時提高了頁面的載入速度。

下圖顯示了開啟了指令碼合併功能後開啟該網站的HTTP請求情況。

 [翻譯]ASP.NET MVC4新特性之指令碼壓縮和合並_ASP.NET

壓縮

壓縮功能實現了對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 選擇你要除錯的指令碼檔案。 

 [翻譯]ASP.NET MVC4新特性之指令碼壓縮和合並_ASP.NET

3. 選擇“配置” 按鈕 ,格式化壓縮後的JavaScript,然後選擇“格式後的JavaScript”按鈕[翻譯]ASP.NET MVC4新特性之指令碼壓縮和合並_ASP.NET

4. 你還可以通過搜尋方法來檢索你需要除錯的函式。 在下面的圖片,在搜尋輸入框中輸入AddAltToImg,搜尋結果會以高亮方式顯示。 

 [翻譯]ASP.NET MVC4新特性之指令碼壓縮和合並_ASP.NET

關於開發人員工具除錯的更多資訊,請參閱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中使用指令碼合併和壓縮功能

  •  優化Web Pages 請點選這裡檢視 Adding Web Optimization to a Web Pages Site.。
  •  優化 ASP.NET Web窗體專案請點選這裡檢視 Adding Bundling and Minification to Web Forms。

在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 包含在專案中。 指令碼合併框架遵循以下幾個原則:

  •  當“FileX.min.js” 和“FileX.js” 同時存在時,在release版本中,系統將選擇檔名包含“.min”的指令碼
  • 請選擇檔名中不包含“.min”的檔案進行除錯。
  • 請過濾檔名中包含“.vsdoc”的檔案(如jQuery-1.7.1-vsdoc.js),該檔案僅僅用於VS的智慧感知提示。

{version}關鍵字是用來包含不同版本的jQuery庫的。 在這個例子中,使用萬用字元存在以下幾個好處:

  • 允許你在不改變原有配置的情況下通過NuGet 更新你的jQuery版本。
  • 自動選擇除錯版本和檔名中帶有“.min”的發行版本。

使用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檔案:

  • Scripts/Common/AddAltToImg.js
  • Scripts/Common/ToggleDiv.js
  • Scripts/Common/ToggleImg.js
  • Scripts/Common/Sub1/ToggleLinks.js

[翻譯]ASP.NET MVC4新特性之指令碼壓縮和合並_ASP.NET

下面的表演示了使用萬用字元來新增如圖所示檔案到專案中:

使用萬用字元

匹配到的檔案或異常提示

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

 

通常情況下,我們應該首選通過完整路徑來新增檔案,其原因如下:

  • 使用萬用字元新增指令碼其新增順序是根據檔名的字母排序來依次新增的,有時候因為指令碼相互依賴的情況,這種新增方式可能會出錯。 CSS和JavaScript檔案經常因為相互依賴而不能按照檔名排序來新增。為了減少潛在的風險,你可以通過新增一個自定義IBundleOrderer來新增檔案,但是通過完整檔名來新增檔案是最保險的。 例如,將來需要新增新檔案時,你可能要通過修改 IBundleOrderer類才能實現。
  • 在JavaScript中使用萬用字元來新增一個目錄的所有指令碼到專案中可能會引起JavaScript的錯誤提示。
  • 在載入CSS檔案時,可能會出現載入重複的情況,如下所示的例子:
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”選項卡快取檔案的情況 :

 [翻譯]ASP.NET MVC4新特性之指令碼壓縮和合並_ASP.NET

請求的路徑為

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專案:

  1. 建立一個資料夾用於新增你的LESS檔案。 下面的示例演示瞭如何使用 Content/MyLess folder。 
  2. 通過NuGet來新增合適的包到你的專案中:
  3. 新增一個類,並實現 IBundleTransform 介面。然後可以通過下面的程式碼把LESS新增到你的專案中: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"; }}
  4. 建立一個Bundle物件,然後新增 LessTransform 和CssMinify ,接下來在 App_Start/BundleConfig.cs 中使用 RegisterBundles  方法來註冊你的方法。var lessBundle = new Bundle("~/My/Less").IncludeDirectory("~/My", "*.less");lessBundle.Transforms.Add(new LessTransform());lessBundle.Transforms.Add(new CssMinify());bundles.Add(lessBundle);
  5. 呼叫Bundle包如下所示:@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

· Howard Dierking

· Diana LaRose

 原文地址:http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification

轉載請註明原文地址:http://www.cnblogs.com/lifeil/archive/2013/03/11/2954071.html