Shoeisha Technology Media

CodeZine(コードジン)

記事種別から探す

Dojo道場 ~ 第11回「Dojo 最新動向 - Asynchronous Module Definition」

「Dojo道場」~実用アプリ構築のためのベストプラクティス

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2012/04/06 14:00

  本稿では、Dojo 1.7の目玉機能として追加された非同期ロードを実現する「AMDローダー」機能について解説します。

目次

はじめに

 Dojoにはモジュールを遅延ロード(lazy load)する機能が備わっており、これをうまく使うと、ページのダウンロードサイズを最小化することによってAJAXアプリケーションのパフォーマンスを最適化することができます。

 Dojo 1.7では、目玉の新機能としてAsynchronous Module Definition(AMDと略されます)ローダーというものが新しく導入され、Dojoがモジュールを遅延ロードする方法、特に非同期にロードする方法が大幅に改善されました。また、Dojo 1.6からDojo 1.7にかけて、AMD APIと呼ばれる標準化された新しいモジュールの定義方法が採用されています。

 ここではこれらのDojoに新しく採用されたAMD API/ローダーについて扱います。

Asynchronous Module Definition(AMD)API

AMD APIとは

 AMD APIはモジュールを定義するAPIで、CommonJSというグループに標準とされるべく提唱されています。このAPIで定義されたモジュールはそれが依存するモジュールを含めて非同期にロードすることができます。これは同期ロードに対し、ブラウザのJavaScript環境にとって次の観点から特に有効です:

  • 同期ロードをすると、その間アプリケーション(ブラウザによってはブラウザ全体も)はユーザが何をしても応答できない(固まってしまう)が、非同期ロードではそれがない。
  • 同期ロードでは同時に1つのHTTPリクエストしか発行できない(上と似た理由によります)が、非同期ロードでは同時に複数のHTTPリクエストを発行できる。これによりコンテンツの量とHTTP接続の数のバランスを最適化することによってアプリケーションのパフォーマンスを向上することができる。
  • 同期ロードで一般的に使われるXMLHttpRequestではセキュリティ上の理由からメインのHTMLファイルが置かれているサーバーとしか通信できないという制約(クロスドメイン制約)があるが、非同期ロードで一般的に使われる動的に<script>タグを作成する方法ではその制約はない。これによりメインのHTMLファイルを自社サーバに置きながら社外サーバに置かれたJavaScriptモジュールを利用することができる。
  • 同期ロードで一般的に使われるXMLHttpRequestで読まれたモジュールはその後eval()という関数を使うことにより初めてJavaScriptとして評価される。eval()で評価されたJavaScriptコードは一般的にデバッグがしにくい。非同期ロードで一般的に使われる動的に<script>タグを作成する方法ではそういったデバッグのしにくさはない。

 AMD APIをサポートするライブラリ(AMDローダーと呼ばれます)はDojo 1.7のほかに(抜粋すると)次のようなものがあります:

 これら2つはどちらもある程度のDojoとの互換性に関する検証がなされています。またDojoと似通ったカテゴリに入るAJAXライブラリのjQueryでも1.7においてAMDのサポートがされたそうです。こういった互換性はAMD APIのもう一つの大きなメリットで、AMDに準拠するどのモジュールも好きなAMDローダーを使ってロードすることができます。

AMD APIの文法

 AMDでは、define()という関数でモジュールを定義します。define()関数は次の形をとります:

リスト1 define()関数の書式
define(id?, dependencies?, factory);
  • id(文字列, 任意): 定義されるモジュールのID。モジュールのIDはdefine()関数内で定義しなくても後述のAMDローダーが自動的に決めてくれるので、省略されることが多い。
  • dependencies(文字列の配列, 任意): 連鎖依存するモジュールのID。
  • factory(関数または任意のオブジェクト, 必須): モジュールを返す関数またはモジュール自体。関数がここで使われた場合、その引数は依存するモジュールとなる。

 モジュールのIDは一般的にモジュールのパスから.jsを取り除いたものとなります。例えばdojo/_base/array.jsのモジュールIDはdojo/_base/arrayとなります。

AMD APIを使ったモジュールの書き方

 上記のAMD APIを使って簡単な価格引き合わせモジュールを書いてみます。これは次の2つのモジュールに依存します:

  • my/pricelist: 商品名に対応する価格。
  • my/discount: 日付に対応する割引率を返す関数。

 これらの実装は次のようになります:

リスト2 my/pricelist.js
define({
    apple: 350,
    orange: 250
});
リスト3 my/discount.js
define(function(){
    var normalrate = 1; // 通常の掛け率
    var specialrate = 0.7; // 3のつく日の掛け率
    return function(d){
        var rate = normalrate;
        if(String(d).indexOf("3") >= 0){
            rate = specialrate; // 3のつく日は3割引!!
        }
        return rate;
    };
});

 どちらの例でもモジュールIDを明示的に定義する必要も連鎖依存するモジュールもないため、define()関数の第一引数(id)、第二引数(dependencies)ともに省略されています。

 my/pricelist.jsの例では第三引数(factory)はりんごが350円、みかんが250円というオブジェクトで、これがモジュールとして定義されます。my/discount.jsの例ではfactoryは定義されるモジュールを返す関数で、その関数の返り値、つまり日付に対して割引率を返す関数がモジュールとして定義されます。

 そして目的の価格引き合わせモジュールは、上の2つのモジュールを依存モジュールとして定義し、次のように書くことができます:

リスト4 my/price.js
define([
    "my/pricelist",
    "my/discount"
], function(pricelist, discount){
    return function(product){
        if(!(product in pricelist)){
            return NaN; // 価格表にない製品
        }
        return pricelist[product] * discount((new Date()).getDate());
    };
});

 この例では、define()関数の第二引数(dependencies)に”my/pricelist”と”my/discount”が指定され、my/priceモジュールがmy/pricelistとモジュールとmy/discountモジュールに依存することが示されています。第三引数(factory)はmy/pricelistモジュールとmy/discountモジュールを引数とする関数となり、その返り値、つまり製品の当日価格を返す関数がmy/priceモジュールとして定義されます。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • 須藤 哲(スドウ アキラ)

    2000年ロータス株式会社(現日本アイ・ビー・エム株式会社)入社, 現在ソフトウェア開発研究所に所属. Lotus iNotesおよびIBM Connections MailのUIフレームワークを中心に携わる.

バックナンバー

連載:「Dojo道場」~実用アプリ構築のためのベストプラクティス

もっと読む

All contents copyright © 2005-2018 Shoeisha Co., Ltd. All rights reserved. ver.1.5