ASP.NET アプリの高速化 : CDN の利用とフォールバック対応

昨今の Web アプリケーションでは jQuery を始め、さまざまな JavaScript ライブラリを使用する機会が多くあります。今や欠かすことのできない JavaScript ライブラリですが、その数やファイルサイズによっては、Web アプリケーションのロード時のパフォーマンスに影響を及ぼすこともあります。特にスマートフォンに代表されるモバイル環境での Web アプリケーション利用が進むにつれ、回線速度や端末の処理能力なども影響して、JavaScript ライブラリを含む Web ページのロード時間はよりクリティカルなポイントになります。

一般的に、Web アプリケーションや Web ページのロード時間やペイロードを削減するために、下記のような方法がとられます。

  • JavaScript/CSS ファイルの縮小化
    主要な JavaScript ライブラリでは縮小版 (*.min.js など) が用意されていますので、これを使ってペイロードを削減することができます。

  • JavaScript/CSS ファイルの結合
    JavaScript や CSS ファイルのロード時の HTTP リクエスト数を減らすため、複数のファイルをサーバーサイドで結合して1つのファイルとしてロードする手法をとることも有効です。

  • CDN の利用
    各アプリケーションに配置されている JavaScript ファイルではなく、CDN (Content Delivery Network) 上に配置されたファイルを使用することも効果的です。

  • キャッシュの活用
    CDN の利用やファイルの結合などと合わせて、ブラウザーキャッシュを意図的に使用する実装も多く取られます(Cache Busting などもそのテクニックの一つ)

  • 遅延ロード
    Web ページのロード時にすべての JavaScript ファイルをロードするのではなく、実際にその JavaScript ライブラリが使用される時まで、そのロードを遅延させる方法です。遅延ロード機能を提供するライブラリも存在します。

  • Vanilla JS を使う
    Vanilla JS って何?という方はこちら “Vanilla JS 使ってる?(JavaScript の高速化)” をご参照ください。つまりは、JavaScript ライブラリを使わずにプレーンな JavaScript API を使いましょう、ということです。JavaScript の実行ではプレーンな JavaScript がもっとも速いことに加えて、JavaScript ライブラリをロードする必要がないので、それにかかるペイロード量は 0 バイトになる訳です。

  • などなど...

ASP.NET における JavaScript ファイルの縮小化や結合処理に関しては、過去のブログ記事 “JavaScript / CSS ファイルの自動縮小・結合処理: ASP.NET MVC 4 新機能シリーズ” (若干内容が古いので更新しないとなのですが・・・)で取り上げているとともに、本ブログ記事の 「ASP.NET MVC 4 で CDN を使う」の項でも簡単に紹介していますのでご参照ください。今回は CDN の利用にフォーカスを当ててご紹介します。

◆ CDN (Content Delivery Network) 経由の JavaScript ライブラリの利用

jQuery などの代表的な JavaScript ライブラリがホストされている CDN サービスとして、主に下記の 2 つが利用できます。

例えば、Microsoft Ajax CDN で jQuery を利用する場合は下記の通りになります。

 <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.0.min.js" type="text/javascript"></script>

一方で、CDN サーバーの問題などで CDN 上のファイルが読み込めない場合にも対応(フォールバック)するため、下記のような記述が使われることも多くあります。

 <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.0.min.js" type="text/javascript"></script>
<script type="text/javascript">
    // フォールバック:CDN が利用できない場合、ローカルパスから jQuery をロード
    (window.jQuery || document.write('<script src="/Scripts/jquery-2.0.0.min.js"><\/script>'));
</script>

     
◆ ASP.NET Web フォーム で CDN を使う

ASP.NET 4/4.5 の環境で Web フォームを利用する場合は、ScriptManager コントロールに EnableCdn プロパティが用意されているので、CDN からのスクリプト参照を容易に実装できます。

 <asp:ScriptManager runat="server" EnableCdn="true" >
    <Scripts>
        <asp:ScriptReference Name="jquery" />
        <asp:ScriptReference Name="jquery.ui.combined" />
    </Scripts>
</asp:ScriptManager>

特に、ASP.NET 4.5 で生成される HTML では、先に紹介したフォールバックへの対応も行われているため、より安心して CDN が利用できるでしょう。

 <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.2.js" type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[
(window.jQuery)||document.write('<script type="text/javascript" src="Scripts/jquery-1.8.2.js"><\/script>');//]]>
</script>

<script src="https://ajax.aspnetcdn.com/ajax/jquery.ui/1.8.24/jquery-ui.js" type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[
(!!window.jQuery.ui && !!window.jQuery.ui.version)||document.write('<script type="text/javascript" src="Scripts/jquery-ui-1.8.24.js"><\/script>');//]]>
</script>

なお、jQuery 2.0.0 や jQuery UI 1.10.2 を使用する場合には、ScriptManager.jQuery アセンブリをアップデートしてください。アップデートは NuGet パッケージ マネージャーで AspNet.ScriptManager.jQueryAspNet.ScriptManager.jQuery.UI.Combined を更新します。

image

また、デフォルトでは Microsoft Ajax CDN を利用しますが、このスクリプトマッピングを変更する手段も用意されています。例えば、Google の CDN を使う場合は、下記のコードを Global.asax の Application_Start などに記述します。

 var mapping = ScriptManager.ScriptResourceMapping;
// Map jquery definition to the Google CDN
mapping.AddDefinition("jquery", new ScriptResourceDefinition
{
    Path = "~/Scripts/jquery-2.0.0.min.js",
    DebugPath = "~/Scripts/jquery-2.0.0.js",
    CdnPath = "https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js",
    CdnDebugPath = "https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.js",
    CdnSupportsSecureConnection = true,
    LoadSuccessExpression = "window.jQuery"
});
 
// Map jquery ui definition to the Google CDN
mapping.AddDefinition("jquery.ui.combined", new ScriptResourceDefinition
{
    Path = "~/Scripts/jquery-ui-1.10.2.min.js",
    DebugPath = "~/Scripts/jquery-ui-1.10.2.js",
    CdnPath = "https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/jquery-ui.min.js",
    CdnDebugPath = "https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/jquery-ui.js",
    CdnSupportsSecureConnection = true,
    LoadSuccessExpression = "window.jQuery && window.jQuery.ui && window.jQuery.ui.version === '1.10.2'"
});

この変更で、ScriptManager で生成される HTML は下記となります。

 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.js" type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[
(window.jQuery)||document.write('<script type="text/javascript" src="Scripts/jquery-2.0.0.js"><\/script>');//]]>
</script>

<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/jquery-ui.js" type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[
(window.jQuery && window.jQuery.ui && window.jQuery.ui.version === '1.10.2')||document.write('<script type="text/javascript" src="Scripts/jquery-ui-1.10.2.js"><\/script>');//]]>
</script>

ASP.NET 4.5 における ScriptManager の情報は下記のブログでも紹介されていますので、こちらもご参照ください。

.NET Web Development and Tools Blog

ASP.NET 4.5 ScriptManager Improvements in WebForms

◆ ASP.NET MVC 4 で CDN を使う

ASP.NET MVC 4 では、System.Web.Optimization 名前空間で提供される、JavaScript や CSS ファイルの縮小・結合機能 (ScriptBundle) を使用できます。

例えば、ASP.NET MVC 4 のプロジェクトテンプレートでは、jQuery のスクリプト参照に Scripts.Render メソッドが使用されています。

 @Scripts.Render("~/bundles/jquery")

そして、BundleConfig.cs では、上記の Render メソッドの引数で指定されている “~/bundles/jquey” 仮想パスから実際にロードさせるスクリプトファイルが指定してされています。

 public static void RegisterBundles(BundleCollection bundles)
{
    bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                "~/Scripts/jquery-{version}.js"));

    ...

ここで使用されているファイル指定 ”~/Scripts/jquery-{version}.js” によって、常にプロジェクトの Scripts フォルダにある jQuery ファイルの最新版がロードされるようになります。実際に生成される HTML の script タグは、一例として下記の通りです。

 <script src="/Scripts/jquery-2.0.0.js"></script>

また、下記のように複数ファイルを指定して、EnableOptimizations = true とするか、Web.config で <compilation debug=”false” /> に設定することで、サーバーサイドで複数のファイルを結合して1つのファイルとしてクライアントサイドから参照させることができます。

 public static void RegisterBundles(BundleCollection bundles)
{
    BundleTable.EnableOptimizations = true;

    bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                "~/Scripts/jquery-{version}.js", 
                "~/Scripts/jquery-ui-{version}.js"));

    ...

ファイルが結合された場合のスクリプト参照は下記のようになり、パラメータにハッシュが付加されます。これは Cache Busting と呼ばれるキャッシュ対応のためのものです(結合元のファイルが変更されると、このハッシュ値も変更される)。

 <script src="/bundles/jquery?v=EDJNFv1yPEKIVEGvRIWIi1R_ZIeljeTGDBeIImFvMlE1"></script>

さて、前置きが若干長くなりましたが、この ScriptBundle 機能でも CDN 参照とフォールバック対応を行うことができます。CDN を使用するだけでしたら System.Web.Optimization アセンブリは 1.0.0 のままで OK ですが、フォールバック対応を有効にする場合は、現在リリースされている Microsoft ASP.NET Web Optimization Framework の 1.1.0-Beta1 が必要になります。こちらは、NuGet パッケージ マネージャーで、検索条件を [リリース前のパッケージを含める] に設定してから更新プログラムを検索すると表示されます。

image

但し、インストール(更新)する前に下記の “Microsoft ASP.NET Web Optimization 日本語リソース” をアンインストールする必要があります。残念ながら 1.1.0-Beta1 と、この日本語リソースは互換性がないためです(現時点で 1.1.0-Beta1 に対応した日本語リソースはリリースされていません)。

image

この日本語リソースをアンインストールすると、依存関係をもった System.Web.Optimization アセンブリ 1.0.0 もアンインストールされますので、改めて、System.Web.Optimization アセンブリ 1.1.0-Beta1 (ID: Microsoft.AspNet.Web.Optimization パッケージ) をインストールします。加えて、WebGrease パッケージの 1.3.0 も必要になりますので、こちらも下記の通り更新してください。

image

これで、下記の通り EnableOptimization プロパティと BundleCollection の UseCdn プロパティを true にして CDN 参照を有効にします。加えて、ScriptBundle クラスコンストラクターの第 2 引数に明示的に CDN の参照パスを指定します。また、フォールバックの有効化は ScriptBundle オブジェクトの CdnFallbackExpression プロパティに CDN からファイルがロードされたか否かの判別に用いるオブジェクト名などを指定します。

 public static void RegisterBundles(BundleCollection bundles)
{
    BundleTable.Bundles.UseCdn = true;    // same as: bundles.UseCdn = true;
    BundleTable.EnableOptimizations = true;

    var jquery = new ScriptBundle("~/bundles/jquery", "https://ajax.aspnetcdnn.com/ajax/jQuery/jquery-2.0.0.min.js").Include(
                "~/Scripts/jquery-{version}.js");

    jquery.CdnFallbackExpression = "window.jQuery";

    bundles.Add(jquery);

    ...

これで、下記のようなスクリプト参照が HTML としてレンダリングされます。

 <script src="https://ajax.aspnetcdnn.com/ajax/jQuery/jquery-2.0.0.min.js"></script>
<script>(window.jQuery)||document.write('<script src="/bundles/jquery"><\/script>');</script>

なお、CDN からの参照になる場合は、先に紹介したファイルの結合処理は行われません。また、残念ながら現バージョンでのフォールバック対応におけるローカルパス参照では、Cache Busting のハッシュ値の付加は行われないようですので何か独自に対応が必要になる場合もあるかもしれませんが、そもそも、CDN サーバーが参照できない場合のフォールバック(非常時対応)ということを考慮すると、ある意味このままでも良いのかもしれませんね。

見落とされがちでもありますが、CDN の利用も時には非常に有効なパフォーマンスチューニング手段になりますので、ASP.NET ベースの Web アプリケーション開発でもぜひご検討ください。