合併與最佳化 - 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的檔案。
我電腦的Visual Studio 2012有ASP.NET and Web Tools 2012.2,開啟專案內的檔案會比較新,也剛好利用上圖來教大家,Bundle時是如何選擇的:
- 選擇有含有".min"名稱的檔案。 這是當jquery-{version}.js與jquery-{version}.min.js都存在時。
- 選擇不含非".min"名稱的檔案。 這是前面所提到,當debug="true"時。
- 不選擇"-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"))
- 透過bundles.UseCdn跟BundleCollection類別說要使用CDN的版本。
- 透過ScriptBundle的建構式傳入CDN路徑。
ScriptBundle類別有兩個建構式,一般都是使用一個參數的建構式,如果使用要CDN必須使用二個參數的建構式:
' 使用CDN版本建構式 Public Sub New(virtualPath As String, cdnPath As String)
這樣設定之後,當我們在開發模式(即debug="true"),會去載入本機端的jQuery。一但我們將網站發佈(即debug="false")就會改使用CDN版本的jQuery。
不過這裡會延伸一個問題,如果已發佈的網站所使用的CDN無法載入,怎麼辦?
我們可以在_Layout.vbhtml或_Layout.cshtml裡留一條退路,新版的Microsoft ASP.NET Web Optimization Framework 1.1.0-alpha1有其他解決方法,這個我們先集中1.0.0版本裡的功能,1.1.0-alpha1等下一篇介紹時再說明。我們先看最原始的處理方法:
<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】,這位作者幫各位把常用的數十種轉換套件準備好了。
沒有留言:
張貼留言
感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。