次の方法で共有



October 2017

Volume 32 Number 10

Web 開発 - スピードのスリル:マネージ AJAX は Web アプリを高速化するか

Thomas Hansen | October 2017

このテーマに関する複数の研究によると、AJAX Web アプリを作成する際に最も重要となる 2 つの懸念事項は、スピードと応答性だといいます。一部の開発者が Web アプリではなく、ネイティブ アプリを作成することを選ぶ理由はいくつかあるでしょう。ですが、読者がおそらくご存知の方法よりも、100 倍高速で 100 倍応答性に優れた AJAX Web アプリを作成する方法があると言ったらどうでしょうか。

筆者は、使用するツールの種類や作成する対象に応じて、アプリの帯域幅使用量を少なくとも 10 分の 1 に削減し、場合によっては 300 分の 1 にまで削減する、純度 100% の JavaScript ベース AJAX Web アプリを作成する方法を考案しました。この手法を「マネージ AJAX」と名付けます。

マネージ AJAX は、マイクロソフトが共通言語ランタイム (CLR) をビルドした方法からヒントを得ています。たとえば、C# アプリケーションを作成する場合、コンパイラによって CLR アセンブリが作成されます。これは、最終結果のランタイム環境が「マネージ環境」になることを示しています。 マネージ AJAX アプリを作成すると、最終結果は標準のプレーンな ASP.NET Web サイト以外にはコンパイルされません。つまり、これがマネージ環境になります。CLR アセンブリを使用しているときに CPU 命令が抽象化されるのと同じように、このマネージ環境では最終結果の JavaScript 部分が完全に抽象化されます。

マネージ AJAX のしくみ

マネージ AJAX には、新しい知識がほとんど必要ありません。ASP.NET 開発を行ったことがあるなら、新しいコントロール ライブラリを .aspx ページに追加し、以前とほぼまったく同じように作業を続けるだけです。.aspx マークアップか、C#/F#/Visual Basic .NET 分離コードのいずれかで、2、3 個の ASP.NET コントロールを作成します。次に、コントロールのプロパティを修飾し、AJAX イベント ハンドラーをいくつか追加したら完了です。

最初のレンダリングでは従来のプレーンな HTML が作成されます。ただし、AJAX 要求中にサーバ側でコントロールに加えた変更がすべて JSON としてクライアントに渡されます。そのため、クライアントは、合計サイズが 5 KB 未満という非常に小さな JavaScript ライブラリだけで済ませることができます。また、5 KB を超える JavaScript を使用することなく、TreeViews、DataGrids、TabViews など、充実した AJAX コントロールを作成できます。

この時点で、既にスタンドアロン JavaScript ファイルとしての jQuery よりも、1 桁近く効率が上回っていることにお気付きでしょうか (縮小および Zopfli で圧縮後の jQuery バージョン 2.1.3 の場合は 30 KB になります)。したがって、ページに jQuery を含めるだけで、他に JavaScript がなくても、マネージ AJAX アプローチを使った場合の 6 倍の帯域幅を既に使用していることになります。独自の JavaScript で jQuery を使用するようになると、この数字が劇的に増加します。

縮小および zip 圧縮されたバージョンの jQuery UI を取り出すと、JavaScript 部分は 73.5 KB 増えます。jQuery と jQuery UI をページに含めるだけで、そのサイズはさらに 103.4 KB 増加することになります (103.4 KB を 4.8 KB で除算すれば、帯域幅の使用量が 21.5 倍になることがわかります)。この時点で、ページ上の 1 つの UI 要素さえまだ作成していません。しかし、jQuery と jQuery UI の組み合わせでは、マネージ AJAX アプローチの約 22 倍の帯域幅が使用されます。また、この 5 KB の JavaScript では、想像できる範囲のほぼすべての UI ウィジェットを作成できます。これには、jQuery と jQuery UI の組み合わせで作成できる UI コントロールもほとんど含まれます。基本的に、何を行うにしても、この 5 KB という JavaScript の上限を超えることはめったにありません。また、HTML と CSS のようなアプリの他の部分も、サイズが大幅に小さくなる可能性があります。

このアプローチを使用すると、AJAX TreeView コントロール、DataGrid コントロール、TabView コントロールなど、豊富な AJAX UI コントロールを作成できます。また、これらのウィジェットを作成する際に、追加の JavaScript を作成する必要はありません。これらのコントロールを使用するために、新しい知識も要りません。ASP.NET の従来の WebControl を使う場合と (ほぼ) 同じ方法で簡単に使用できます。

マネージ AJAX アプローチには、ページへの HTTP 要求を処理するハンドラーが 2 通りあります。1 つ目のハンドラーは、標準のプレーンな HTTP 要求です。これは単純に HTML をクライアントに渡します。もう 1 つのハンドラーでは、HTTP POST パラメーターの有無をチェックできます。このパラメーターが存在する場合、ハンドラーでは各コントロールに加えられた変更のみを、要求元のクライアントに対して JSON としてレンダリングします。AJAX 要求中、ページのコントロール階層を変更することで作成される各コントロールは、自動的に作り直され、以前の要求に含まれていたプロパティが設定されます。クライアント側には、コントロールの JSON プロパティの処理と包括的な属性の更新を行う汎用ハンドラーに加え、DOM 要素の DOM イベント ハンドラーがあります。

このアプローチにはプラスの副作用が多数あります。たとえば、HTML 要素を単なる HTML 要素としてレンダリングすることにより、その Web が本来作成された形との間で対立が起こりません。これにより、Web アプリのセマンティクスが (たとえば、ロボット型検索エンジンから) 理解されやすくなります。また、両方の長所を生かしたい場合、このアプローチでは、クライアント上の要素を実際に変更するための優れた環境が実現されます。

さらに、このアプローチを、カスタム JavaScript と必要なだけ組み合わせることもでき、その時点で、プレーンな HTML 要求でレンダリングされた HTML を簡単に検査することができます。この方法に比べ、他の多くの AJAX UI ライブラリで使用される「div の呪文」によるアプローチではたいてい、DataGrids と TreeViews を作成するためにメガバイト単位の JavaScript が格納されます。

したがって、スピードと応答性が 100 倍高くなるという計算が誇張ではないことをご理解いただけるでしょう。実際、最も一般的に使用されているコンポーネント UI ツールキットのうち、C# および ASP.NET と併用されているものすべてと比較しても、多くの場合、帯域幅の使用に関するスピードと応答性は 100 ~ 250 倍高くなります。最近、System42 のマネージ AJAX TreeView ウィジェットと、ASP.NET スタックの最大級のコンポーネント ツールキットのうち 3 つを使用して、その間でパフォーマンス測定を行いました。その帯域幅使用量におけるスピードと応答性の差は、150 ~ 220 倍くらいになることが分かりました。

これが意味することも説明するために、非常に低速なインターネット接続につないでいることを想像してください。その接続では、5 KB の JavaScript をダウンロードするのに 1 秒かかります。これは、マネージ AJAX JavaScript をダウンロードするには 1 秒かかり、他の一部のツールキットの JavaScript 部分をダウンロードするには 3 分 40 秒もの時間がかかる可能性があることを示しています。言うまでもありませんが、2 つの eコマース ソリューションをこの 2 つのアプローチを使用して構築した場合、コンバージョンにどれくらいの差が生じるかを想像してみてください。

コードの確認

説明は十分でしょう。ここからは実際にやってみます。まず、bit.ly/2u5W0EO.(英語) から Phosphorus Five をダウンロードします。次に、p5.sln ファイルを開いて、p5.ajax.dll アセンブリに取り掛かれるように Phosphorus Five をビルドします。Web アプリ内では p5.ajax.dll を参照として使用することになるため、これをビルドしてから次に進む必要があります。Phosphorus Five は 30 を超えるプロジェクトで構成されています。ただし、ここでは、p5.ajax プロジェクトにのみ注目します。

次に、新しい ASP.NET Web サイトを Visual Studio で作成します。必ず Web フォーム アプリケーションを作成します。Visual Studio for Mac では、[ファイル]、[新しいソリューション]、[その他]、[.NET]、[ASP.NET Web フォーム プロジェクト] の順に移動します。

新たに作成した Web サイトの内部で、ビルド済みの p5.ajax.dll アセンブリへの参照を作成し、web.config を次のコードのように変更します。

<?xml version="1.0"?>
<configuration>
  <system.web>
    <pages clientIDMode="Static">
      <controls>
        <add assembly="p5.ajax" namespace="p5.ajax.widgets" tagPrefix="p5" />
      </controls>
    </pages>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
</configuration>

このコードで重要な部分は、"clientIDMode" と "add assembly" です。 この時点で、p5 をプレフィックスとして使用することにより、.aspx マークアップ内の任意の p5.ajax コントロールを使用できます。Default.aspx ページのコンテンツを図 1 のコードのように変更します。

図 1 クリックされるとテキストが変わる 1 つのボタンを含むページの作成

<%@ Page Language="C#" Inherits="foobar.Default" %>
<!DOCTYPE html>
<html>
<head runat="server">
  <title>Default</title>
</head>
<body>
  <form id="form1" runat="server">
    <p5:Literal 
      runat="server"
      id="foo"
      onclick="foo_onclick"
      Element="button">Click me!</p5:Literal>
  </form>
</body>
</html>

次に、その分離コードを以下のように変更します。

using System;
namespace foobar
{
  public partial class Default : p5.ajax.core.AjaxPage
  {
    [p5.ajax.core.WebMethod]
    public void foo_onclick(p5.ajax.widgets.Literal sender, EventArgs args)
    {
      sender.innerValue = "Hello World from Managed Ajax!";
    }
  }
}

まず、AjaxPage からページを継承し、WebMethod 属性をイベント ハンドラーに追加して、イベント ハンドラーの最初のパラメーターを Literal ウィジェットとして明示的かつ厳密に型指定します。この時点で、Web サイトを開始し、ボタンをクリックすれば、結果を確認できます。Web サイトをデバッグしているときに奇妙なバグが発生したら、Visual Studio の [Browser Link] 設定がオフになっていることを確認します。通常、これはツール バーのボタンで、Visual Studio の最上部に表示されます。ここで行われていることに興味がある方は、HTTP 要求を調べてみてください。また、最初の HTML も必ず確認します。

ここまでのまとめ

実際のマネージ AJAX とはこのようなものです。このアイデアには、考慮すべき重要な点がいくつかあります。第 1 に、ページ上のコントロールのプロパティは、ページ内の AJAX イベント ハンドラーから変更できます。Element 型の p など、別の Literal ウィジェットを作成した場合は、そのコンテンツをボタンの foo_onclick イベント ハンドラーから更新することもできます。

第 2 に、適切と思われる任意の方法でウィジェットのプロパティを動的に追加、削除、更新、または取得することができます。属性またはイベント ハンドラーを参照するには、単純に C# の添字演算子を使用します。図 2 では、ウィジェットの innerValue を設定するのではなく、style プロパティをチェックし、この CSS スタイルのプロパティを使用して黄色の背景を切り替えています。ウィジェットの style プロパティは永続化して "記憶" できることがわかります。また、クライアントとサーバーの間で大量の ViewState を相互に受け渡しすることなく、この処理が行われていることもわかります。

実際のアプリケーションでは CSS クラスを使用する場合もあるでしょう。これは、図 2 の参照を style から class に交換することで対応できます。 ただし、今回の例のシンプルさを保つために、ここでは CSS ファイルを混入せず、代わりに利便性を考えて style 属性を使用しています。図 2 に示すアプローチを使用すると、ページ上のウィジェットで、任意の属性を追加、削除、および変更できます。

図 2 背景色の切り替え

using System;
namespace foobar
{
  public partial class Default : p5.ajax.core.AjaxPage
  {
    [p5.ajax.core.WebMethod]
    public void foo_onclick(p5.ajax.widgets.Literal sender, EventArgs args)
    {
      if (sender.HasAttribute ("style"))
        sender.DeleteAttribute ("style");
      else
        sender ["style"] = "background-color:Yellow;";
    }
  }
}

第 3 のポイントは、おそらくこれが最も重要ですが、必要に応じて、新しい AJAX コントロールを他のウィジェットに、動的に追加、削除、更新、および挿入できることです。ただし、この最後のポイントを確認する前に、「三位一体のウィジェット」を調べる必要があります。

p5.ajax には 3 つの異なるウィジェットがあります。ただし、これらはそれぞれの API が非常に似ています。構成を使ってこの 3 つのウィジェットを組み合わせることで、自由に HTML マークアップを作成できます。図 2 の例は Literal ウィジェットを使用しています。Literal ウィジェットには innerValue プロパティがあり、このプロパティはクライアント側で innerHTML にマップされます。また、このウィジェットを使用すると、コンテンツを 1 つの文字列または HTML として簡単に変更できます。

Container ウィジェットには、複数のウィジェットを格納できます。また、Container ウィジェットはコントロールのコレクションを記憶し、AJAX 要求中に、コントロールのコレクションを動的に追加、削除、または変更できるようにします。

3 番目のウィジェットは Void ウィジェットです。これは、HTML 入力要素、br 要素、hr 要素など、コンテンツを一切持たないコントロールにのみ使用します。今回の例で最も重要なのは、おそらく Container ウィジェットでしょう。それでは、作業を続け、.aspx ページのコードを図 3 のように変更します。

図 3 1 つのリスト アイテムを含む箇条書きリストと 1 つのボタンを備えたページの作成

<%@ Page Language="C#" Inherits="foobar.Default" %>
<!DOCTYPE html>
<html>
<head runat="server">
  <title>Default</title>
</head>
<body>
  <form id="form1" runat="server">
    <p5:Literal 
      runat="server"
      id="foo"
      onclick="foo_onclick"
      Element="button">Click me!</p5:Literal>
  <p5:Container
      runat="server"
      id="bar"
      Element="ul">
      <p5:Literal
        runat="server"
        id="initial"
        onclick="initial_onclick"
        Element="li">Initial list element, try clicking me!</p5:Literal>
  </p5:Container>
  </form>
</body>
</html>

図 3 のウィジェット階層では、1 つの button と、li 子要素を持つ ul 要素を作成しています。次に、C# の分離コードを図 4 のコードに変更します。

図 4 AJAX イベント ハンドラーをマッピングすることによる新しいリスト アイテムの作成

using System;

namespace foobar
{
  public partial class Default : p5.ajax.core.AjaxPage
  {
    protected p5.ajax.widgets.Container bar;

    [p5.ajax.core.WebMethod]
    public void foo_onclick(p5.ajax.widgets.Literal sender, EventArgs args)
    {
      // Using the factory method to create a new child widget for our "ul" widget.
      var widget = bar.CreatePersistentControl<p5.ajax.widgets.Literal>();
      widget.Element = "li";
      widget.innerValue = "Try clicking me too!";

      // Notice we supply the name of the method below here.
      widget ["onclick"] = "secondary_onclick";
    }

    [p5.ajax.core.WebMethod]
    public void initial_onclick(p5.ajax.widgets.Literal sender, EventArgs args)
    {
      sender.innerValue = "I was clicked!";
    }

    [p5.ajax.core.WebMethod]
    public void secondary_onclick(p5.ajax.widgets.Literal sender, EventArgs args)
    {
      sender.innerValue = "I was ALSO clicked!";
    }
  }
}

この最後のコードでは、新しいウィジェットが Container ウィジェットに動的に挿入されているのがわかります。基本的に、新しい li 要素は、AJAX 要求中に動的に ul 要素に追加されます。これは造作なく動作しました。また、これらのウィジェットは、そのプロパティを変更でき、なおかつイベント ハンドラーを呼び出せるような方法で、AJAX 要求全体にわたって永続的に記録されます。また、Element プロパティを介して HTML 要素をレンダリングでき、添字演算子を使用して属性を追加することができます。

これで、HTML マークアップを 100% 完全に制御できるようになりました。適切だと思われる任意の方法で、必要に応じてページ上の要素をすべて更新する、非常に小さな AJAX 要求と応答を作成することができます。そのうえ、これを 4.8 KB の JavaScript で実現しました。Web アプリ AJAX 開発は、プレーンな従来の Windows フォーム開発と同じくらい簡単に実行できるものになります。また、このプロセスでは、最終的な Web アプリが 100 倍高速で、100 倍応答性の高いものになります。

Hyperlambda での演習

数か月前に書いた MSDN マガジン 2017 年 6 月号のコラム「C# - Hyperlambda を使って C# をより動的にする」(msdn.com/magazine/mt809119) では、従来とは異なるプログラム言語 Hyperlambda と、その基礎を成す実行ツリーについて説明しました。これを取り上げたのは、Hyperlambda のツリー ベースのアプローチを使用すれば非常に簡単に AJAX ウィジェットの階層を宣言できるためです。p5.ajax と Hyperlambda を組み合わせて、AJAX TreeView ウィジェットを使用すると、いくつか優れた効率性が見えてきます。

これを調べてみましょう。まず、Phosphorus Five のほか、bit.ly/2vbkNpg (英語) の手順に従って System42 をダウンロードし、これをメインの p5.webapp フォルダーに配置します。次に、非常に高速な AJAX TreeView ウィジェットが含まれている System42 を起動し、"CMS" を開きます。[+] をクリックして新しいラムダ ページを作成したら、図 5 に示すコードを貼り付けます。

図 5 ディスク上のフォルダー間の移動が可能になる AJAX TreeView の作成

create-widget
  parent:content
  widgets
    sys42.widgets.tree
      crawl:true
      items
        root:/
      .on-get-items
        list-folders:x:/../*/_item-id?value
        for-each:x:/@list-folders/*?name
          list-folders:x:/@_dp?value
          split:x:/@_dp?value
            =:/
          add:x:/../*/return/*
            src:@"{0}:{1}"
              :x:/@split/0/-?name
              :x:/@_dp?value
          if:x:/@list-folders/*
            not
            add:x:/../*/return/*/items/0/-
              src
                class:tree-leaf
        return
          items

[Settings] をクリックし、空のテンプレートを選び、[OK] をクリックしてページを保存します。次に、[View] ページをクリックします。

HTTP 要求の通信全体の状況を調べながら、AJAX TreeView の拡張を試みます。すると、この 24 行の Hyperlambda を使って AJAX TreeView を参照するフォルダーを構築しただけで、p5.webapp フォルダーの各フォルダーが表示されるようになり、なおかつその最初の帯域幅の合計使用量がわずか 10.9 KB であることがわかります。

これらの結果と他の AJAX ツールキットを比べた場合、たいてい、他のツールキットでは数メガバイトの JavaScript に加えて、通信でやり取りされる他のすべての要素をダウンロードする必要があります。一方、Hyperlambda TreeView ではわずか 4.8 KB の JavaScript しか使用されていないことがわかるでしょう。

この AJAX TreeView は、純粋な Hyperlambda の合計 717 行のコードでビルドされました。Literal ウィジェット、Container ウィジェット、および Void ウィジェット以外は使用していません。このコードの大部分はコメントで構成されているため、AJAX TreeView コントロールを作成するために必要なコードは 300 行ほどでした。ウィジェットを使う際には 24 行の Hyperlambda が使用されました。これにより、ディスク上のフォルダーを参照できます。しかし、他の方法を使用してこのコントロールを作成するなら、数千行のコードが必要になることでしょう。さらに、そのコントロールを使用するためには数百行のコードが必要になります。これを、図 5 では 24 行のコードで実現しています。

お望みなら、すぐに Hyperlambda のサンプル コードを 3 行交換して、最終的に独自の特殊な Active Event カスタム ウィジェット使用することもできます。これにより、1 行のコードで特殊なウィジェットを使用できるようになります。その方法については、bit.ly/2t96gsQ (英語) を参照してください。

そのため、サーバーのフォルダーを参照する AJAX TreeView を 1 行のコードで作成できるようになります。他のツールキットで同等のものを作成するには、一般的に、4 つの異なる言語の数百行のコードが必要になります。これが、1 つのプログラミング言語の 1 行のコードで実現されます。それも、競合する方法に比べて最大 300 倍効率よく実行されます。

まとめ

100 倍優れた結果が得られると考えてください。つまり、100 倍高速で、結果が 100 倍最適化され、品質が 100 倍向上し、バグが 100 分の 1 に減ります。その結果、以前に比べて生産性が 100 倍上がります。必ず最新のデータを使用するために、Phosphorus Five は bit.ly/2uwNv65 (英語) からダウンロードし、System42 は bit.ly/2vbkNpg (英語) からダウンロードしてください。


Thomas Hansen は、1982 年に Oric-1 コンピューターを使用してコードを記述し始めた 8 歳の頃から、ソフトウェアを作成してきました。たまに、デメリットよりもメリットが上回るコードを作成しています。彼は、Web、AJAX、アジャイルの方法論やソフトウェア アーキテクチャに情熱を傾けています。連絡先は、thomas@gaiasoul.com (英語のみ) です。

この記事のレビューに協力してくれたマイクロソフト技術スタッフの James McCaffrey に心より感謝いたします。


この記事について MSDN マガジン フォーラムで議論する