Ext JS 6 で複数環境に対応する方法

こんにちは、療養中の @martini3oz です。

Sencha から Ext JS 6 の GA が発表になってから、しばらく経ちますが、 このブログでは、まだ Ext JS 6 の話題を扱えていませんでした。

Ext JS 6 の目玉はいくつかあるのですが、最も大きな点は、Ext JS と Sencha Touch が「マージされた」ということでしょう。 以前から Sencha フレームワークをお使いの方は、「マージ」ってどういう風に?と思われることでしょう。 大まかに言うと次のようになります。

  • Ext JS と Touch のクラスシステムの微妙な違いを統一した
  • core パッケージという、共通部分を担うパッケージができた
  • classic と modern という二つの「ツールキット」を用意した
  • Sencha Cmd で、それぞれのツールキットを切り替えられるようにした
  • microloader で実行するビルドを切り替えられるようにした

クラスシステムの微妙な違いはこまりものでした。UI に関係のない Model の定義ですら違いがあったので、Ext JS と Touch の両方で使うクラスを書くことはほぼ不可能でした。クラスシステムの統一と core パッケージによって、これが解消されたのは非常に大きな事です。

そして、Ext JS 5 は classic というツールキットに、Touch は modern というツールキットになりました。 ツールキットは、ビジュアルクラス部分で、それぞれデスクトップ用、モバイル用と分類されますが、別な視点で言うと、レガシー用、HTML5用とも言えます。 Ext JS 6 の現在のバージョンでは、modern の方は Touch をそのまま移植した程度のコンポーネントになっていて、classic にはあるけど modern にはないコンポーネントが多数ありますが、これも徐々に増えていくそうです。

この「マージ」によって、一つのソースコードでデスクトップからスマートフォンまですべてに対応したアプリケーションを作ることが可能になったのでしょうか。 現状で言うと、「限りなくそれに近い」という答えになります。 classic と modern は別個のツールキットなので、一つの製品のなかに両者を混在することはできません。 しかし、両者の共通部分は一つのソースコードで記述できます。 また、classic と modern という二つのビルドはできるのですが、microloader の働きで、アクセスしてきたデバイスに応じて、読み込むビルドを自動的に選択してくれます。

レガシーブラウザからスマートフォンまで、すべてのデバイスで動作する、ユニバーサルアプリケーションを作る方法について、以下、Sencha のガイド Developing for Multiple Environments and Screens を翻訳しましたので参考にしてください。(翻訳ミスなどみつけたら教えてください)

複数の環境への開発

すべてのデバイスで 機能性に富み、使いやすく美しいアプリケーションを作ることは、 アプリケーション開発における理想ですが、なかなか成し遂げることは難しいことです。 Ext JS 6 によって、あなたはほんの少しの苦労でこの目的を達成するのに必要なツールを手にします。 accomplished in a few different ways. これらの要件を満たすアプリケーションの生成は、いくつかの方法があります。

ユニバーサルアプリケーションとツールキット

Ext JS 6 での大きな変更点の一つは、Ext JS と Touch が一つのフレームワークにマージされたことです。 フレームワークには、二つの独立したツールキット (classc と modern) があります。 ツールキットには Ext JS と Touch のビューレイヤーを含みます。 コアとなるリソースやロジックを共有し、双方のツールキットを利用するアプリケーションを「ユニバーサルアプリケーション」と呼びます。

アプリケーションのツールキットを選択するには、 Sencha Cmd で生成したアプリケーションの app.json ファイルを次のように調整するだけです。

"toolkit": "classic", // or "modern"

双方のフレームワークのコア (データ、コントローラー、モデルなど) は、 一つの共通のプラットフォームに調整されました。 これにより、共有のデータやロジックは、よりいっそうアプリケーションに最適化できるようになります。

: ツールキットについてより詳しくは、後ほど言及します。

app.json はご存じのように、アプリケーションのコアの設定リストです。

Sencha Cmd で生成された app.json ファイルには多くのプロパティがあります。 これらのプロパティの説明は、インラインでコメントに記述されています。

プロジェクトをアップグレードした場合には、app.json の中には全てのオプションが含まれないことがあります。 アップグレードを実行したあと、そこに記述されないプロパティのデフォルト値は、 “.sencha/app/app.defaults.json”にあります。 このファイルを編集するのではなく、そこから必要な部分を app.json にコピーしてください。

app.json について詳しくは the microloader guide. をご覧ください。

classic ツールキット

classic ツールキットは、伝統的な Ext JS 5 アプリケーションのサポートを提供します。 デスクトップブラウザ、タブレット、タッチスクリーン付きのラップトップのサポートを含みます。 詳しくは、 What’s New Guide をご覧ください。

modern ツールキット

modern ツールキットは、デスクトップからスマートフォンまでの全てのモダンブラウザ (IE111以降) をサポートする一般的な HTML5 アプリケーションを提供します。 詳しくは、 What’s New Guide. をご覧ください。

ビルドプロファイル

ビルドプロファイルにより、アプリケーションのバリエーションを生成することができるようになります。 バリエーションは、アプリケーションの app.json ファイル内にある builds オブジェクトで指定します。 これまでにも、テーマやロケールといったもので違うビルドをする機会がありました。

上記の app.json について詳しくは、 the microloader guide. を参照してください。

Ext JS 6 と Sencha Cmd 6 では、toolkit キーをビルドコンフィグに指定できます。 これで、classic または modern ツールキットセットを設定できます。 Cmd は、各ビルドキーをループして、適切なツールキットとツールキットに依存しないロジックを適用します。

ツールキットに依存しないコードは、ツールキット間で共有されるコード全てです。 また sencha-core パッケージ (データ、コントローラーなど) にあるものを含みます。 この共有ロジックは双方のツールキットで使われるということをよく覚えておいてください。

ユニバーサルアプリケーションの builds コンフィグは、次のような JSON 構造をしています。

"builds": {
    "mymodern": {
        "toolkit" : "modern",
        "theme"   : "theme-cupertino",
        "requires": [
            "charts",
            "ux"
        ]
    },
    "myclassic": {
       "toolkit" : "classic",
       "theme"   : "theme-neptune",
       "slicer"  : null,
       "requires": [
            "charts"
        ]        
    }
}

ビルド時に予期すべき事

では上記の build オブジェクトによって、起こることを解説しましょう。 この builds コンフィグにより、 Sencha Cmd で 次のビルドコマンドを発行することができます。

// modern アプリのみをビルドする
sencha app build mymodern
// classic アプリのみをビルドする
sencha app build myclassic
// builds オブジェクト内の全ての対象をビルドする
sencha app build

ビルドターゲットの名前は、自由に設定できることを覚えておいてください。 mymodernfoo に置き換えた後に次のように実行することもできます。

sencha app build foo

mymodern アプリケーションの結果は、modern ツールキットが使われ、 ux と charts パッケージにアクセスし、Cupertino テーマで表示されます。

myclassic アプリケーションの結果は、classic ツールキットが使われ、 charts パッケージにアクセスし、Neptune テーマを使います。

色々なバリエーションを builds オブジェクトに設定できます。 デフォルトでは、すべては builds フォルダのルートに出力されます。

ユニバーサルアプリケーション

ユニバーサルアプリケーションは、両方のツールキットにわたる複数のビルドを生成します。 アプリケーションのこれらのビルド結果は、 デスクトップやモバイルデバイスで実行されます。 一つのアプリケーションが、一つの classpath に modern と classic フレームワークをインクルードすることは できない ということを覚えておいてください。 ほとんどの場合、アプリケーションにはグローバルロジックを保管する従来のアプリケーションディレクトリ、ルートレベルの app フォルダ、があります。 理想的には、アプリケーションの共有部分には、データ、モデル、ビューモデルなどを保管します。 また、 ツールキットにわたるロジックを生成したいばあいなどでは共有コントローラーも配置できます。

例えば、classic アプリケーションにグリッドがあり、modern アプリケーションにはリストがあり、両方とも共有のストアとモデルのデータを使うとしましょう。 次に、両方のアプリケーションにおいて、コンポーネントへのユーザー操作に対応させたいとします。 そうするとグリッドとリストで共通のイベントを探す必要があります。 一つの解決方法は、双方のコンポーネントが発火できる select イベントを使うことです。 しかし、ロジックに“itemtap”がある場合、classic アプリケーションでは、それがどういう意味かわからず、Cmd のビルドプロセスの classic の部分で問題が発生します。

共有のロジックとリソースを検討したら、 開発者は、適切なツールキット src フォルダに独自のコードを書きます。 これが、Ext JS 6 の新しいユニバーサルアプリケーションのフォルダ構造をみる時の考え方です。

注: 単一のツールキットのアプリケーションを生成した場合のフォルダ構造は、以前の実装と非常に良く似ています。

Ext JS 6 フォルダ構造

ユニバーサルアプリケーションを生成する際の、 Ext JS 6 と Sencha Cmd 6 の新しいディレクトリ構造の大きな違いについて覚えておいてください。 これらは、アプリケーションが次の三つの特定のエリアを特定できるようにするために変更されました。

  • グローバルアプリケーションコード

  • classic 特定のコード

  • modern 特定のコード

次が、ユニバーサルアプリケーションのフォルダ構造です。

// app
     // app
          // model
          // store
          // view
     // classic
          // src
          // sass
          // resources
     // modern
          // src
          // sass
          // resources
     // resources
     // sass

ユニバーサルアプリケーションを生成する

ユニバーサルアプリケーションを作るのは単純です。 Sencha Cmd は、適切なファイル構造、app.json、共有コードのサンプルを持った、「スターターアプリ」を生成します。 ユニバーサルアプリケーションを生成するには、次のようにします。

sencha -sdk /path/to/ExtSDK generate app MyApp ./MyAppLocation

Sencha Cmd は、ユニバーサルアプリケーションの全機能を生成します。 このアプリケーションは、 “app”フォルダに共有データを置いた classic と modern アプリケーションを生成します。 それぞれのツールキット特有の定義は、ツールキット名のフォルダ (modern と classic) にあります。

開発時に各アプリケーションを見るには、sencha app watch で、ビルドの対象を指定するのと同じ方法を使って指定できます。

sencha app watch modern
sencha app watch classic

注: 対象を指定しなかった場合は、デフォルトで builds オブジェクト内の対象のビルドになります。

アプリケーションをビルドすると、それぞれのブラウザーで見ることができます。 たとえば、ビルドフォルダーを iPhone でロードすると、アプリケーションの modern バージョンが表示されます。それをデスクトップブラウザでロードすると、アプリケーションの classic バージョンが表示されます。

実行時の設定

また、アプリケーションを生成するアプリケーションの実行環境を使う為のいくつかの方法があります。 これらの方法には、App プロファイル、レスポンシブコンフィグ、フラットフォームコンフィグがあります。

それでは、それぞれが、どのように共同して、シームレスな体験をユーザーに提供するのか可能性を探索してみましょう。

App プロファイル

Ext.app.Profie を使うと、mainView (または Viewport) に定義された条件に従って、アプリケーションのビューをスワップアウトできます。 これは、特定の条件でプロファイルをアクティブにすることで、アプリケーションに完全に違うビューを生成できると言うことです。 たとえば、モバイルデバイスかデスクトップブラウザのどちらにロードされたかによって特定のビューを表示したい事があると思います。

この場合、二つのプロファイルを作って、 Application クラスにある profiles 配列に列挙します。 次は、簡単な例です。

Ext.define('App.Application', {
    extend: 'Ext.app.Application',
    profiles: [
        'Desktop',
        'Mobile'
    ]
});

アクティブなプロファイルは、プロファイルの isActive メソッドの戻り値によって特定されます。 次が、検出された OS によって、デスクトップビューがロードされるように設定するサンプルです。

Ext.define('App.profile.Desktop', {
    extend: 'Ext.app.Profile',

    mainView: 'App.view.desktop.Main',

    isActive: function () {
        return Ext.os.is.Desktop;
    },

    launch: function () {
        console.log('Launch Desktop');
    }
});

プロファイルの mainView コンフィグは必須ではありません。 代わりに、プロファイルの launch メソッドを使って、より細かな制御をすることができます。 アクティブなプロファイルの launch メソッドだけが呼び出されます。

プラットフォームコンフィグ

platformConfig プロパティはクラス定義に記述することも、 インスタンスのコンフィグオブジェクトに指定する事もでき、 現在のプラットフォームまたはデバイスの分類によって、コンフィグを切り替えます。 次のように使います。

Ext.define('App.view.summary.Manufacturing', {
    extend: 'Ext.panel.Panel',

    title: 'Mfg Summary',

    platformConfig: {
        desktop: {
            title: 'Manufacturing Summary'
        }
    }
});

上記は、次のような直接的なアプローチと同じ結果になります。

Ext.define('App.view.summary.Manufacturing', {
    extend: 'Ext.panel.Panel',

    title: testForDesktop ? 'Manufacturing Summary'
                          : 'Mfg Summary'
});

これは、platformConfig と三項演算子のメリットを比較しているのではなく、 platformConfig がクラス定義の一部として見なされることを示しています。 このようにこのアプローチはベースクラスに関係なく動作します。 インラインロジックよりも platformConfig がいい理由は、 データのみのビューを維持でき、JSON フォーマットで安全に送信できるからです。

platformConfig をインスタンスコンフィグに指定する事もできます。

Ext.define('App.view.summary.Manufacturing', {
    extend: 'Ext.panel.Panel',

    items: [{
        xtype: 'panel',

        platformConfig: {
            desktop: {
                title: 'Manufacturing Summary'
            },
            '!desktop': {
                title: 'Mfg Summary'
            }
        }
    }]
});

上記の直接的な書き方は次のようになるでしょう。

Ext.define('App.view.summary.Manufacturing', {
    extend: 'Ext.panel.Panel',

    items: [
        Ext.merge({
            xtype: 'panel'
        },
        testForDesktop ? {
            title: 'Manufacturing Summary'
        } : {
            title: 'Mfg Summary'
        })
    ]
});

この platformConfig の使い方では、マージ処理は initConfig メソッドによって処理されます。 言い換えると、コンフィグに platformConfig プロパティを提供した場合、 コンストラクターで initConfig を呼び出しているクラスでのみサポートされるということです。 Ext.WidgetExt.Component、多くのデータパッケージクラス (AbstractStore など) や Observable を使う全てのクラスがこれに当てはまります。

クラス定義での platformConfig がクラス定義を変更するやり方と同様に、インスタンスコンフィグでの platformConfig は、オブジェクトの初期設定を変更します。

レスポンシブコンフィグ

Ext JS 5 から responsiveConfig導入され。 、レスポンシブ Mixin と plugin が使えるようになりました。 responsiveConfig のルールとプロパティは、インスタンスが生成された時だけでなく、デバイスの向きやビューポートのサイズが変わったときにも評価されます。 これは、platformConfig に較べオーバーヘッドが増えることになりますが、 自分でウィンドウのリサイズや向きの変更を監視することで処理するより、より能率的です。

縦長の画面の時に、デバイスの分類 (“desktop”) ではなく、デバイスのサイズに応じて、タイトルを変更することができます。

Ext.define('App.view.summary.Manufacturing', {
    extend: 'Ext.panel.Panel',
    mixins: ['Ext.mixin.Responsive'],

    responsiveConfig: {
        'width >= 600': {
            title: 'Manufacturing Summary'
        },
        'width < 600': {
            title: 'Mfg Summary'
        }
    }
});

上記では responsiveConfig を使っているので、 ここでは mixin を使い、各インスタンスに plugin を設定しなくてすんでいます。 コンポーネントインスタンスで使う場合は、レスポンシブ plugin を代わりに使います。

Ext.define('App.view.summary.Manufacturing', {
    extend: 'Ext.panel.Panel',

    items: [{
        xtype: 'panel',
        plugins: 'responsive',

        responsiveConfig: {
            'width >= 600': {
                title: 'Manufacturing Summary'
            },
            'width < 600': {
                title: 'Mfg Summary'
            }
        }
    }]
});

ここでの width は、コンポーネントの幅ではなく、ビューポートの幅であることは、よく覚えておいてください。

まとめ

これらのツールで、特定のシチュエーションでの正しいツールを選択すると言うことです。 ロードタイムをチューニングするには platformConfig。 より動的な条件には、responsiveConfig。 大きなスケールで変更するには、Ext.app.Profile

そして、タブレットとデスクトップさらにはスマートフォンにおいて完全に違うルックスのアプリケーションが必要な場合は、Sencha Cmd のビルドプロファイルを検討してください。 ビルドプロファイルでは、デスクトップビルドからタブレットのオーバーヘッドを排除できますし、その逆も同様です。

「ひとつのサイズで全てに合わせる」ことはできないので、Ext JS は、能率と柔軟性をまとめたいくつかのツールを提供しています。 それらはアプリケーションが幅広いデバイスに自然にフィットするように、一緒に使うことができます。

“Ext JS 6 で複数環境に対応する方法” への1件の返信

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です