[翻譯]ASP.NET MVC4新特性之腳本壓縮和合並

[翻譯]ASP.NET MVC4新特性之腳本壓縮和合並

目前主流瀏覽器限制客戶端對同一域名只能同時發起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 包含在項目中。 腳本合併框架遵循如下幾個原則:

  •  當「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

下面的表演示了使用通配符來添加如圖所示文件到項目中:

使用通配符

匹配到的文件或異常提示

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」選項卡緩存文件的狀況 :

 

請求的路徑爲

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 、LESSCoffeeScript並轉換併合併到優化包中。 例如,添加 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

相關文章
相關標籤/搜索