ASP.NET MVC 4 - 合併與最佳化(bundling and minification)補充資料

合併與最佳化 - 1.1.0 Alpha 1

在ASP.NET MVC 4網站開發美學第5.6小節談到【合併與最佳化】這個主題,內容中討論的相關功能就是由【System.Web.Optimization.dll】所提供,以前必須由擴充套件來達到的功能,現在都由ASP.NET MVC 4內建的System.Web.Optimization.dll提供,我查了一下書中的截圖,在Visual Studio 2010 RTM版本內版本為1.0.0-beta2,應該是安裝了Visual Studio 2012 Update 1,所以目前開啟的專案已內已經是版本1.0.0。以下會補充一些書藉沒有的內容,共有二篇補充資料,此為第一篇,第二篇會討論Microsoft ASP.NET Web Optimization Framework 1.1.0-alpha1這個下一版本又會提供那些新功能。

啟用合併與最佳化的影響

http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification提供了一分資料,有使用B/M與沒有使用B/M的差異:

項目 使用B/M 未用B/M 改善
檔案請求 9 34 256%
KB傳送 3.26 11.92 266%
KB接收 388.51 530 36%
載入時間 510 MS 780 MS 53%

讀者可以看到,未使用【合併與最佳化】之前與使用之後,第一個好處是,請求數量大量的減少,第二個好處是,傳送與接收大小明顯減小。

啟用或關閉合併與最佳化的二種方法

這書裡有教,不過如果網頁的讀者也想使用合併與最佳化,當然也是必須教各位一下。

【合併與最佳化】在專案預設情況下是被關閉的,因為我們還在開發階段,但為求保險,可能開發到了一個段落或上線前,可以先把合併與最佳化的功能打開,看看合併與最佳化功能啟用後,是否會影響到所撰寫的CSS與JavaScript。

方法一:透過web.config設計

   <system.web>
       <compilation debug="true" />
       <!-- 以下略 -->
   </system.web>
  

debug="true"修改為debug="false"

方法二:透過修改BundleTable.EnableOptimizations屬性

找到【~\App_Star\BundleConfig】組態檔,在程式碼加上:

  Public Class BundleConfig
      ' 如需 Bundling 的詳細資訊,請造訪 http://go.microsoft.com/fwlink/?LinkId=254725
      Public Shared Sub RegisterBundles(ByVal bundles As BundleCollection)
    ' 以上省略
    BundleTable.EnableOptimizations = True
      End Sub
  End Class
  

透過程式方法,在開發階段可以有更大的彈性。

關於{version}佔位符號

在書中未特別交代{version}這個佔位符號,以下補充說明一下。

    bundles.Add(New ScriptBundle("~/bundles/jquery").Include(
            "~/Scripts/jquery-{version}.js"))
  

在設定Bundles規則時,可以看到jquery-{version}.js這一段設定中使用了一個{version}佔位符號,它會到你所指定的資料夾下去掃瞄所有檔案名稱為jquery-*.js的檔案。

jQuery程式檔清單

我電腦的Visual Studio 2012有ASP.NET and Web Tools 2012.2,開啟專案內的檔案會比較新,也剛好利用上圖來教大家,Bundle時是如何選擇的:

  1. 選擇有含有".min"名稱的檔案。 這是當jquery-{version}.js與jquery-{version}.min.js都存在時。
  2. 選擇不含非".min"名稱的檔案。 這是前面所提到,當debug="true"時。
  3. 不選擇"-vsdoc"或".intellisense"的檔案。 Visual Studio更新後提供的是*.intellisense.js,不再是"-vsdoc"檔名。

以圖片的專案來說,因為含有".min"名稱的檔案有較高優先順序,所以jquery-1.8.2.min.js會被選擇拿來合併。使用了{version}佔位符號後的好處有:

  • 使用NuGet的更新jQuery的版本,不會影響到Bundles規則或任何參數jQuery的View頁面。
  • 當網站發佈時(release)時,debug會自動切換至false且自動選擇".min"版本的檔案。

使用CDN載入

我們在合併與最佳化功能中能優先使用CDN的版本:

  'bundles.Add(New ScriptBundle("~/bundles/jquery").Include(
  '           "~/Scripts/jquery-{version}.js"))

  bundles.UseCdn = True
  ' http://www.asp.net/ajaxlibrary/cdn.ashx#Using_jQuery_from_the_CDN_16
  ' https://developers.google.com/speed/libraries/devguide#jquery
  Dim jqCdnPath As String = "http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.8.0.js"
  bundles.Add(New ScriptBundle("~/bundles/jquery", jqCdnPath).Include(
              "~/Scripts/jquery-{version}.js"))
  
  1. 透過bundles.UseCdn跟BundleCollection類別說要使用CDN的版本。
  2. 透過ScriptBundle的建構式傳入CDN路徑。

ScriptBundle類別有兩個建構式,一般都是使用一個參數的建構式,如果使用要CDN必須使用二個參數的建構式:

   ' 使用CDN版本建構式
   Public Sub New(virtualPath As String, cdnPath As String)
  

這樣設定之後,當我們在開發模式(即debug="true"),會去載入本機端的jQuery。一但我們將網站發佈(即debug="false")就會改使用CDN版本的jQuery。

Load jQuery file from CDN

不過這裡會延伸一個問題,如果已發佈的網站所使用的CDN無法載入,怎麼辦?

我們可以在_Layout.vbhtml或_Layout.cshtml裡留一條退路,新版的Microsoft ASP.NET Web Optimization Framework 1.1.0-alpha1有其他解決方法,這個我們先集中1.0.0版本裡的功能,1.1.0-alpha1等下一篇介紹時再說明。我們先看最原始的處理方法:

我們參考http://weblogs.asp.net/jgalloway/archive/2010/01/21/using-cdn-hosted-jquery-with-a-local-fall-back-copy.aspx的解決方案:

  <body>
      @RenderBody()

      @Scripts.Render("~/bundles/jquery")
      <script type="text/javascript">
          if (typeof jQuery == 'undefined') {
              var e = document.createElement('script');
              e.src = '@Url.Content("~/Scripts/jquery-1.8.2.js")';
              e.type = 'text/javascript';
              document.getElementsByTagName("head")[0].appendChild(e);

          }
      </script>
      @RenderSection("scripts", required:=False)
  </body>
  

測試jQuery型別是否存在,不存在就代表載入有問題,即可產生一組script元素載入伺服端的jQuery。

Include方法與IncludeDirectory方法

在Bundle類別,Include()是一個字串陣列,例如,jQuery UI預設值:

   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方法,從命名可以很直接的看出以目錄為單位來設定規則:

   ' 多載一
   Public Function IncludeDirectory(directoryVirtualPath As String, searchPattern As String) As System.Web.Optimization.Bundle
   ' 多載二
   Public Function IncludeDirectory(directoryVirtualPath As String, searchPattern As String, searchSubdirectories As Boolean) As System.Web.Optimization.Bundle
  

使用範例:

   bundles.Add(New StyleBundle("~/Content/themes/base/jqui").IncludeDirectory(
             "~/Content/themes/base", "jquery.ui.*"))
  

如果非常確定某一目錄下的所有內容都要進行合併與最佳化,使用IncludeDirectory方法會比Include方法快速許多。

@Scripts.Url()方法

在View頁面輸出時,可以使用@Scripts.Render()與@Scripts.Url():

   ' Render方法定義
   Public Shared Function Render(ParamArray paths() As String) As System.Web.IHtmlString
   ' Url方法定義
   Public Shared Function Url(virtualPath As String) As System.Web.IHtmlString
  

Render可以傳入多個虛擬路徑當參數,Url只能傳入單一個虛擬路徑當參數。注意,此處的虛擬路徑是指Bundleconfig組態檔中設定的虛擬路徑。在ASP.NET MVC 4的View裡預設使用Render方法,但是如果只有單一組設定,可以考慮使用Url方法,另外使用Url方法還可以得到另一個HTML5的好處:

   @* @Scripts.Render("~/bundles/modernizr") *@
   <script src='@Scripts.Url("~/bundles/modernizr")' async> </script>
  

這裡我們引用了HTML5在script元素裡的關鍵字async,這樣就可以非同步的載入此Script。

合併支援SCSS, Sass, CoffeeScript, LESS…

如果我們的專案有使用一些非W3C的CSS與JavaScript技術,例如,SCSS, Sass, CoffeeScript, LESS…等,一般在撰寫完成後,都必須先轉換成CSS或JavaScript,才能進行測試。目前在NuGet上已經有很多套其他開發者寫好的套件,讓我們可以直接轉換,在進行合併時,會先將如SCSS轉換為CSS,TypeScript轉換為JavaScript,然後再進行合併的動作。

讀者可以依自己習慣使用的技術到NuGet下關鍵字搜尋,這裡介紹一個【NuGet:Bundle Transformer】,這位作者幫各位把常用的數十種轉換套件準備好了。

小結

當你的前端設備來源越來越多元,當你的前端使用的外掛套件越來越多,你真的要好好考慮一下這類合併與最佳化的技術。ASP.NET MVC 4裡所提供的合併與最佳化(Bundling and Minification)真的是一個不可多得的好功能。

沒有留言:

張貼留言

感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。