JavaScript / CSS ファイルの自動縮小・結合処理: ASP.NET MVC 4 新機能シリーズ
今回は、ASP.NET MVC 4 / ASP.NET 4.5 新機能の JavaScript / CSS ファイルの自動縮小・結合処理についてご紹介します。
◆ 概要
クライアントサイドにおける JavaScript や CSS ファイルのロード時のペイロード削減とパフォーマンス向上のために、JavaScript や CSS ファイルのスペースや改行の削除(縮小化)と、複数ファイルの結合(単一ファイル化)を自動でおこなうことができるようになりました。
ASP.NET MVC 4 Beta の Internet Application などのテンプレートでは、JavaScript や CSS ファイルの縮小化と結合処理が組み込まれています。下記は Internet Application テンプレートの _Layout.cshtml の head 要素内の記述です。
1: <head>
2: <meta charset="utf-8" />
3: <title>@ViewBag.Title - My ASP.NET MVC Application</title>
4: <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
5:
6: <link href="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Content/css")"
7: rel="stylesheet" type="text/css" />
8: <link href="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Content/themes/base/css")"
9: rel="stylesheet" type="text/css" />
10: <script src="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Scripts/js")"></script>
11:
12: <meta name="viewport" content="width=device-width" />
13: </head>
例えば、これまでは参照する JavaScript のファイルパスを下記のように静的に記述してファイルをロードしていました。
1: <script src="@Url.Content("~/Scripts/jquery-1.6.4.min.js")"
2: type="text/javascript"></script>
ここで jQuery のバージョンを 1.7.1 に変更する場合などでは、プロジェクトの jQuery ファイルを NuGet などで更新すると同時に script 要素の参照ファイル名も変更する必要があり、変更し忘れる恐れがありました(私もよく忘れます)。
一方、ASP.NET MVC 4 ではスクリプトの参照パスを ~/Script/js としておくだけで、対象となるスクリプトファイルの最新バージョンを自動で縮小化・結合してロードさせることができます。
1: <script
2: src="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Scripts/js")">
3: </script>
ここで、縮小化と結合処理の対象とするファイルは、下記の記述により Global.asax.cs の Application_Start で RegisterTemplateBundles メソッドを使うことで、標準のプロジェクトテンプレートの /Content と /Scripts フォルダー内の 特定のファイル に対して縮小化と結合処理がおこなわれるように指定できます。
1: protected void Application_Start()
2: {
3: ...
4:
5: BundleTable.Bundles.RegisterTemplateBundles();
6: }
この RegisterTemplateBundles が対象とする特定のファイルに関しては下記の情報をご参照ください。
ASP.NET Forums: RegisterTemplateBundles vs EnableDefaultBundles
この結果、JavaScript や CSS の縮小化と結合処理がサーバーサイドにて自動でおこなわれ、クライアントサイドにはそれぞれ単一のファイルとしてロードされるようになります。
1: <head>
2: <meta charset="utf-8" />
3: <title>PC Home Page - My ASP.NET MVC Application</title>
4: <link href="/favicon.ico" rel="shortcut icon" type="image/x-icon" />
5:
6: <link href="/Content/css?v=vdyJ8j6PezNabuhTYE0PXk95ugyRYLm39WByHWTNF3g1"
7: rel="stylesheet" type="text/css" />
8: <link href="/Content/themes/base/css?v=UM624qf1uFt8dYtiIV9PCmYhsyeewBIwY4Ob0i8OdW81"
9: rel="stylesheet" type="text/css" />
10: <script src="/Scripts/js?v=IAp9l9PymSYK1lFihtC_bf8uBdqJDE7ms_s2OwIFRzs1"></script>
11:
12: <meta name="viewport" content="width=device-width" />
13: </head>
14:
◆ ResolveBundleUrl によるキャッシュへの対応
この縮小化と結合機能は下記のように、ResolveBundleUrl を使用せずに単純に ~/Script/js を指定するだけで動作します。
※ ASP.NET MVC 4 (ASP.NET Web Pages 2) では、Razor エンジンがルート演算子 “~” を認識して自動処理するようになりました。これによって、@Url.Content(...) の記述を省略することが可能となっています。
1: <script src="~/Scripts/js" type="text/javascript"></script>
この場合、出力される HTML マークアップは下記のようになります。
1: <script src="/Scripts/js" type="text/javascript"></script>
一方、前述のとおり ASP.NET MVC 4 におけるプロジェクトテンプレートでは ResolveBundleUrl メソッドを使用しています。
1: <script
2: src="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Scripts/js")">
3: </script>
この場合の HTML マークアップは下記のようにハッシュ値がパラメータに付加された形となります。
1: <script src="/Scripts/js?v=IAp9l9PymSYK1lFihtC_bf8uBdqJDE7ms_s2OwIFRzs1"
2: type="text/javascript"></script>
これは、一般に Cache Busting と呼ばれる、JavaScript や CSS が更新された場合のキャッシュへの対応のためで、対象のファイルが更新されるとこのハッシュパラメータも更新される仕組みになっています。これにより、JavaScript /CSS ファイルを更新した場合でも、キャッシュの影響を受けずに正しく最新版がロードできるようになっています。
◆ カスタム JavaScript / CSSの縮小化と結合処理
標準のプロジェクトテンプレートの記述では、前述の RegisterTemplateBundles メソッドにより、/Content と /Scripts フォルダー内の特定のファイルに対して縮小化と結合処理がおこなわれるようになっており、このフォルダー内に独自に追加したファイルは処理の対象になりません。
独自に追加したファイルも処理の対象にする場合、Global.asax.cs ファイルの Application_Start で呼び出していた RegisterTemplateBundles を、下記のように EnableDefaultBundles に変更します。
1: protected void Application_Start()
2: {
3: ...
4:
5: BundleTable.Bundles.EnableDefaultBundles();
6: }
これで、/Content と /Scripts フォルダー内のすべてのファイル を縮小化と結合処理の対象とすることができます。但し、この場合にはプロジェクトの対象フォルダー内にある不要なファイル(例えばデバッグバージョンなど)も処理の対象となることに注意してください。
一方、/Contents と /Scripts フォルダー以外の JavaScript や CSS ファイルを対象としたい場合には、下記のように Application_Start で Bundle オブジェクトを BundleTable に追加します。
1: protected void Application_Start()
2: {
3: ...
4:
5: var myCss = new Bundle("~/MyStyles/css", new CssMinify());
6: myCss.AddDirectory("~/MyStyles", "*.css", true);
7: BundleTable.Bundles.Add(myCss);
8:
9: var myJs = new Bundle("~/MyScripts/js", new JsMinify());
10: myJs.AddDirectory("~/MyScripts", "*.js", true);
11: BundleTable.Bundles.Add(myJs);
12: }
この場合、例えば ~/MyScripts の次の 2 つのファイルに対する縮小化および結合処理の結果は下記のようになります。
~/MyScripts/ShowMe.js
1: // MyScript
2: function ShowMe(name) {
3: alert(name);
4: }
~/MyScripts/Plugin.js
1: // jQuery Plugin: doubleSizeMe
2: (function ($) {
3: $.fn.doubleSizeMe = function () {
4: return this.each(function () {
5: var $this = $(this),
6: width = $this.width(),
7: height = $this.height();
8:
9: $this.width(width * 2);
10: $this.height(height * 2);
11: });
12: };
13: })(jQuery);
パス ~/MyScripts/js の参照
1: <script
2: src="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/MyScripts/js")">
3: </script>
縮小化・結合処理にてロードされたスクリプトファイルの内容(縮小・結合処理結果)
1: function ShowMe(n){alert(n)}(function(n){n.fn.doubleSizeMe=function(){return this.each(function(){var t=n(this),r=t.width(),i=t.height();t.width(r*2),t.height(i*2)})}})(jQuery)
以上のようにして、ASP.NET MVC 4 / ASP.NET 4.5 では、Web アプリケーションで使用する JavaScript や CSS ファイルのペイロードの最適化を容易におこなえるようになっています。
参考:System.Web.Optimization Namespace
https://msdn.microsoft.com/en-us/library/system.web.optimization(VS.110).aspx