Dojo 1.5 を使用してオンデマンドで外部 JavaScript ライブラリーをロードする

そして Web アプリケーションのロード時間を短縮する

Dojo は RIA (Rich Internet Application) を作成するための優れたライブラリーです。しかし、複雑な Web 2.0 アプリケーションでは複数の JavaScript ライブラリーを必要とする場合がよくあり、ページのロード時に大量のライブラリーをロードするとパフォーマンスのオーバーヘッドが大きくなる可能性があります。この記事では、Dojo の dojo.io.script メソッドを使用して非同期、オンデマンドで JavaScript ライブラリーをロードする方法について説明します。サンプル・コードは外部 JavaScript ライブラリーのロードを簡潔に「ラップする」上で役立ちます。

Nick Maynard, Web 2.0 Consultant, IBM

Nick Maynard はイギリスの Hursley にある IBM Software Solutions Transformation チームで働いています。彼の専門は、Dojo、Ajax、Web プログラミング、Web サービス、Linux などです。



2012年 4月 26日

概要

JavaScript の Dojo ツールキットは優れたライブラリーであり、RIA (Rich Internet Application) を作成するための大部分の要件を満たすことができます。しかし場合によると、あるアプリケーションのニッチな要件や高度な要件を満たすために、外部の JavaScript ライブラリーが必要となる場合があります。そうした場合、<script> タグの追加という標準的な HTML の手法でアプリケーションにライブラリーを追加することもできますが、そうするとパフォーマンスのオーバーヘッドが大きくなります。幸いなことに、Dojo にはその方法に代わり、dojo.io.script が用意されています。

この記事では、dojo.io.script を使用する方法の長所と短所について説明します。サンプル・コードでは、大きな落とし穴のいくつかを避ける方法を示します。この記事で使用するソース・コードはダウンロードすることができます。

外部 JavaScript ライブラリーをロードするための 2 つの方法

表 1 は外部 JavaScript ライブラリーをロードするための 2 つの方法を比較しています。

表 1. <script> タグと dojo.io.script
<script> タグdojo.io.script
ページのロード速度への影響ロードの速度に大きく影響する可能性があり、ライブラリーのロードによってページの起動がブロックされます。ロードの速度への影響はなく、ライブラリーはオンデマンドでロードされます。
コードの実行速度への影響影響はありません。ライブラリーを最初に使用する際、そのライブラリーがロードされる間は実行速度が遅くなります。
複雑さ単純であり、すべてのコードは即座にライブラリーを使用することができます。複雑であり、コードはインテリジェントにライブラリーを要求し、ライブラリーがロードされるのを待つ必要があります。

dojo.io.script を使用する方法は、処理が分岐されて特定のコード部分が実行される場合にのみ外部ライブラリーを使用するアプリケーションにとっては、明らかなメリットがあります。特に、遅延ローディングを行う場合にメリットがあります。アプリケーション全体でライブラリーが使用される場合には、<script> タグによる方法には単純さという強力なメリットがあります。dojo.io.script を使用する方法には、どのような場合にもページのロード時間が短縮されるというメリットがあることから、dojo.io.script を使用することを検討するとよいかもしれません。


dojo.io.script を使用する

リスト 1 に、dojo.io.script を使用して 1 つのライブラリーを非同期でロードする単純な例を示します。

リスト 1. dojo.io.script を 1 度だけ呼び出す
dojo.require("dojo.io.script");

dojo.addOnLoad(function() {
    // Load the library.
    // dojo.io.script.get is asynchronous, so we get back a dojo.Deferred object
    var deferred = dojo.io.script.get({url : "url_of_library.js"});

    // Handle the deferred callback paths
    deferred.then(function() {
        // This function is called when the library is successfully loaded
        // You should place your application code that depends on the library here
    }, function() {
        // This function is called if an error occurs
    });
});

高度な使い方

複数のモジュールを含み、モジュール間に依存関係があるライブラリーの場合には、上記で概略を示したような呼び出しの連鎖によって、目的とするロード動作を実現する必要があるかもしれません。リスト 2 に、より高度な呼び出しの連鎖の例を示します。

リスト 2. dojo.io.script の呼び出しの連鎖
dojo.require("dojo.io.script");

dojo.addOnLoad(function() {
    // Load a JavaScript library consisting of two modules
    // We construct a partial function to load the second module; 
    // This simplifies the code path
    var deferred = dojo.io.script.get({url : "url_of_first_module.js"})
        .then(dojo.hitch(dojo.io.script, 'get', {url : "url_of_second_module.js"}));
    
    // Deferred handling goes here 
});

複雑さに対処する

複数モジュールで構成されるライブラリーを dojo.io.script を使用してロードする場合や、コード内の複数の場所でライブラリーを使用する場合、dojo.io.script の処理や、dojo.io.script によって遅延ロードされるオブジェクトの処理が、複雑になる場合があります。あるモジュールを複数の場所で使用する場合には、そのモジュールを 2 度ロードしてしまわないように、特に注意する必要があります。

複数モジュールで構成されるライブラリーをロードしている場合や、複数の場所でライブラリーを使用している場合には、中間的な手段を使用することを検討してください。


libproxy

libproxy を使用する方法では、モジュール間の依存関係を処理し、モジュールが 1 度しかロードされないように保証します。リスト 3 のサンプル・コードは libproxy の使い方を示しています。

リスト 3. libproxy を使用する (libproxy_example.html)
dojo.require("proxy.jsv");

dojo.addOnLoad(function() {
    var jsvProxy = new proxy.jsv();
    
    jsvProxy.load('json-schema-draft-01').then(function() {
        // At this point, the JSV module for validating against draft 1 has been loaded.
        alert("loaded JSV draft 1");
    });
});

リスト 4 に、Gary Court による JSV (JSON Schema Validator) ライブラリーのロードを、libproxy を使用してラップする方法を示します。

リスト 4. JSV ライブラリーのロードをラップする (proxy/jsv.js)
dojo.provide("proxy.jsv");

dojo.require("com.ibm.developerworks.libproxy");

/**
 * 
 */
dojo.declare("proxy.jsv", [com.ibm.developerworks.libproxy], {

    /**
     * In this file, we define the structure of our library,
     * and its inter-module dependencies
     */
    constructor : function() {
        var jsvRoot = "https://raw.github.com/garycourt/JSV/master/lib";
      
        this.modules = {
            '_uri' : { 
                sources : [{url : jsvRoot + "/uri/uri.js"}]
            },
            'base' : { 
                sources : [{url : jsvRoot + "/jsv.js"}], 
                deps : ['_uri']
            },
            'json-schema-draft-01' : { 
                sources : [{url : jsvRoot + "/json-schema-draft-01.js"}], 
                deps: ['base']
            },
            'json-schema-draft-02' : { 
                sources : [{url : jsvRoot + "/json-schema-draft-02.js"}], 
                deps: ['base'] 
            },
            'json-schema-draft-03' : { 
                sources : [{url : jsvRoot + "/json-schema-draft-03.js"}], 
                deps: ['base'] 
            }
        };
    }
    
});

リスト 5 は libproxy のコードそのものを示しています。

リスト 5. ライブラリーをラップするための基本的な実装 (com/ibm/developerworks/libproxy.js)
dojo.provide("com.ibm.developerworks.libproxy");

dojo.require("dojo.io.script");
dojo.require("dojo.DeferredList");

/**
 * Do not directly use this class; instead, extend it and redefine the
 * modules field in the constructor
 */
dojo.declare("com.ibm.developerworks.libproxy", [], {

    constructor : function() {
        this._moduleDeferreds = [];
    },

    /**
     * Holds the modules of the library (and optional references to their
     * dependencies).
     * 
     * NB: 'sources' and 'deps' arrays will be loaded in parallel. If you need
     * to serialise this, add another module layer. ie. uri.js MUST be loaded
     * before jsv.js below
     * 
     * The 'sources' array holds objects that are passed in their entirety to
     * dojo.io.script.get()
     * 
     * Example, { 'module1Ref': { sources: [{ url: "module1.js" }] },
     * 'module2Ref': { sources: [{ url: "module2.js" }], deps: ['module1'] } };
     */
    modules : null,

    /**
     * Returns a dojo.Deferred object whose callback/errback fires when the
     * module is ready for use. Callback chain will contain the module
     * reference.
     * 
     * Example usage: load('module2').then(function() {});
     */
    load : function(/* String */moduleRef) {
        var F = this.declaredClass + ".";
        console.debug(F + "load()", arguments);

        // Check cache - have we loaded this library module before?
        if (this._moduleDeferreds[moduleRef]) {
            return this._moduleDeferreds[moduleRef];
        }

        // Create a new deferred and cache it
        var deferred = this._moduleDeferreds[moduleRef] = new dojo.Deferred();

        var module = this.modules[moduleRef];
        if (module) {
            deferred.callback(moduleRef);
            if (module.deps) {
                // Load dependencies in parallel
                deferred = deferred.then(dojo.hitch(this,
                        function(/* Array */dependencies) {
                            var defs = dojo.map(dependencies, dojo.hitch(this,
                                    'load'));
                            return new dojo.DeferredList(defs, false, true);
                        }, module.deps));
            }
            // Load sources in parallel
            deferred = deferred.then(dojo.hitch(this,
                    function(/* Array */sources) {
                        var defs = dojo.map(sources, dojo.hitch(dojo.io.script,
                                'get'));
                        return new dojo.DeferredList(defs, false, true);
                    }, module.sources));
        } else {
            deferred.errback("Unknown module reference.");
        }

        return deferred;
    }
});

まとめ

複数の JavaScript ツールキットと JavaScript ライブラリーを使用する場合、ページのロード時間への対処が直ちに問題になりがちです。この記事では <script> タグに代わる方法を説明しました。Dojo の dojo.io.script 機能を使用すると、ライブラリーを「遅延」方式でロードすることによって、ページのロードに要する時間を短縮することができます。また、この方法によって複雑になる場合の対策として、依存関係とロードの処理を行うための中間手段に libproxy を使用する方法を説明しました。


ダウンロード

内容ファイル名サイズ
Sample, wrapping JSV library using libproxysample.zip4KB

参考文献

学ぶために

製品や技術を入手するために

  • IBM 製品の評価版: IBM 製品の評価版をダウンロードするか、あるいは IBM SOA Sandbox のオンライン試用版で、DB2、Lotus、Rational、Tivoli、WebSphere などが提供するアプリケーション開発ツールやミドルウェア製品を試してみてください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=811120
ArticleTitle=Dojo 1.5 を使用してオンデマンドで外部 JavaScript ライブラリーをロードする
publish-date=04262012