リソース管理機能仕様 [ver.1.2]
JavaScriptオブジェクトやCSS・テンプレートファイルなど各種リソースの依存関係の解決・管理を行うリソース管理機能の仕様を説明します。
- リソース機構概要
- リソースの登録(h5.res.register())
- リソースの取得(h5.res.dependsOn().resolve())
- cssファイルの解決
- jsファイルの解決
- ejsファイルの解決
- 名前空間の解決
- h5.settings.res
- 使用されるリゾルバ
リソース機構概要
リソース機構は、JavaScriptオブジェクト、JavaScriptファイル、CSSファイル、テンプレートファイルなどのリソースを解決するための機構です。
動的にリソースを取得したい場合や、必要なリソースをHTMLへの記述ではなくJavaScriptコード側で管理したい場合に有用です。
リソースの登録(h5.res.register())
リソースを登録するには、h5.res.register()を使用します。
第1引数はリソースキー、第2引数はそのキーに対応する値(オブジェクトなど)、
第3引数はそのオブジェクトをグローバルに見えるようにするかを指定します。(デフォルト:false)
trueが指定された場合、keyの名前でオブジェクトを公開します。
(ドット区切りに対応して自動的に名前空間オブジェクトが生成されます。)
第3引数がtrueかつ第4引数が与えられた場合、グローバルに公開するときには第4引数の名前で公開します。
これにより、keyは単純な名前にしつつ、グローバルに公開するときには特定の名前空間以下に公開する、ということが可能です。
すでに存在する名前(キー)のリソースを登録しようとした場合
キーが重複した場合、リソースは上書きされます。
(フレームワークではログを出力します。)
リソースの取得(h5.res.dependsOn().resolve())
依存関係の解決の流れは以下のようになります。
- h5.res.dependsOn()メソッドにリソースキーを渡して、Dependencyオブジェクトを生成する。
- 生成したDependencyオブジェクトのresolve()メソッドを呼んでリソースを取得する。
h5.res.dependsOn()は第1引数にリソースキーと呼ばれる文字列を取ります。リソース管理機構はこのリソースキーによって、JavaScriptオブジェクトなのかCSSなのかテンプレートファイルなのかを判断し、依存関係の解決方法を決定します。
Dependency.resolve()はプロミスオブジェクトを返します。取得したリソースは返ってきたプロミスにdoneハンドラを登録して取得してください。
Dependency.resolve()を呼んだ時に内部で実行される依存関係を解決する関数をリゾルバと呼びます。自分でリゾルバを作成することもできますが、デフォルトで以下のリゾルバが用意されています。
- cssファイル解決
- jsファイル解決
- ejsファイル(テンプレート)解決
- 名前空間(JavaScriptオブジェクト)の解決
どのリゾルバが使用されるかはリソースキーによって決定されます。
複数のリソースの同時取得
複数のリソースを同時に取得したい場合は、h5.res.dependsOn()に、リソースキーを配列の形で渡します。
配列で指定されたリソースが全て取得できると、プロミスが完了します。
プロミス完了時の戻り値は以下のようになります。
//doneハンドラの第1引数に、指定されたリソース全てが
//キーの順番通りに配列で渡される。
var a1 = all[0];
var b1 = all[1];
//第2引数以降には、キーの順番通りに、指定されたリソースが個別に渡される。
alert(a1 === a); //true
alert(b1 === b); //true
});
依存関係のチェーン(ネストした依存関係)
リソース管理機能は、「あるリソースAがBに依存していて、BはさらにCに依存している」
(A,B,Cはそれぞれ別ファイルに存在)という場合にも対応しています。
この場合、リソースAの依存を解決する(h5.res.dependsOn('A').resolve())と、
a.js ⇒ b.js ⇒ c.js と順番に必要なファイルを読み込んですべての依存を解決してから
全体の依存性解決が完了します。
このため、a.js~c.jsでは、それぞれ必要なモジュールをh5.res.register()で正しく登録する必要があるので注意してください。
(例えばb.jsでBをregister()し忘れると、依存性解決に失敗します。)
なお、register()でコンポーネントを登録した後、依存元のdone()ハンドラが呼び出されるのは
b.jsの初期読み込みが全て終わった後になります。
例:
(function(){
h5.res.dependsOn('ObjB').resolve().done(function(objB){
alert(objB.value); //1と表示される
});
})();
//b.js
(function(){
var objB = {
value: 0
};
//register()呼び出し時のobjB.valueの値は0だが、
//dependsOn('ObjB')のdoneハンドラが実行されるのは
//b.jsの初期読み込みが完了した後、つまり
//次のobjB.value=1;が実行された後になる。
h5.res.register('ObjB', objB);
objB.value = 1;
})();
コード例
(function(){
//AはBに依存
h5.res.dependsOn('B').resolve().done(function(b){
function A(){
b();
}
});
h5.res.register('A', A);
})();
//b.js
(function(){
//BはCに依存
h5.res.dependsOn('C').resolve().done(function(c){
function B() {
c();
}
h5.res.register('B', B);
});
})();
//c.js
(function(){
//Cは依存無し
function C(){
}
h5.res.register('C', C);
})();
内部挙動(仕様外)
resolve()が呼ばれたら、解決待ち状態を保持。
register()が呼ばれると、当該オブジェクトを内部のコンポーネントMapに登録した後
解決待ちのうち解決完了可能になったものがないかどうかをチェック。
解決完了可能になったものは完了する。
完了待ちにならなかったものはそのままにする(次にregister()されたときに再度チェック)。
また、解決待ちMapに入った(dependsOn().resolve())のに一定秒数以上リソースのdependsOn()がない
(=ネストした依存関係がもうないのに解決できない)リソースについては、その解決を失敗させる。
多重度の関係は以下の通り。
[Dependency] * - * [コンポーネント] * - * [ファイル]
よって、あるコンポーネントを要求するDependencyは複数存在する可能性があるので注意。
cssファイルの解決
リソースキーにcssファイルのパスを渡すと、resolve()を呼んだ時に指定されたcssファイルを読み込むlinkタグを作成し、headタグに追加します。これによって指定したcssファイルのスタイルが適用されます。
jsファイルの解決
リソースキーにjsファイルのパスを渡すと、resolve()を呼んだ時に指定されたjsファイルをロードして、ロードしたスクリプトを実行します。既にロード済みのjsファイルはロードされません。
ejsファイルの解決
リソースキーにejsファイルのパスを渡すと、resolve()を呼んだ時に指定されたejsファイルをロードして、以下のようなオブジェクトをdoneハンドラに渡します。
path: ロードしたファイルのパス,
url: ロードしたファイルのURL(絶対パス),
templates: [{id: テンプレートID, content: テンプレートの中身}, ...]
}
ロードしたejsファイルをViewオブジェクトに適用したい場合は、Viewのregister()メソッドを使ってテンプレートIDとテンプレートの中身を登録してください。
また、コントローラの__templatesプロパティに、ejsファイルをリソースキーに指定したDependencyオブジェクトを指定することもできます。この場合は、このコントローラのViewにロードしたejsファイルのテンプレートが適用されます。h5.settings.res.baseUrlの設定(後述)がデフォルトであれば、ejsファイルのパスを記述する場合と同様の動作をします。
__name: 'SampleController',
__templates: h5.res.dependsOn('templates/sample.ejs'),
__ready: {
/** this.viewにsample.ejsに記述されたテンプレートが適用される。パス指定した場合と同じ。 **/
var str = this.view.get('sample1');
}
};
名前空間の解決
h5.res.dependsOn()に名前空間文字列をリソースキーに指定すると、名前空間を解決するDependencyオブジェクトが返ってきます。
resolve()を呼ぶと、リソースキーに基づいてJavaScriptオブジェクトを取得して返します。
名前空間'app.controller.HogeController'を取得したい場合は、以下のソースコード例のように記述します。
var dependency = h5.res.dependsOn('app.controller.HogeController');
dependency.resolve().done(function(result){
// resolve()を呼ぶと、app.controller.HogeControllerがグローバルに公開される
// result === app.controller.HogeController
h5.core.controller(document.body, app.controller.HogeController);
});
resolve()を呼んだ時の名前空間の解決は以下のような仕組みで行われます。
- 指定された名前空間にオブジェクトが公開済みかどうか
- →公開済みならそのオブジェクトを返して終了。
- 公開されていない場合は、名前空間とカレントパス設定(後述)をみて、動的にロードするjsファイルを決定する
- ロードしたjsファイルによって、指定した名前空間にオブジェクトが公開されていたら、そのオブジェクトを返す
ロードされるjsファイル
名前空間解決時に動的にロードされるjsファイルは、名前空間をディレクトリ構造とみなして、カレントパスから辿ったファイルになります。名前空間文字列の'.'をディレクトリの区切り文字に変換し、最後に'.js'を付けたものをロード対象のjsファイルと見做します。
カレントパスはh5.settings.res.baseUrlで設定したものを使用します(後述)。デフォルトは'./'となっており、
現在表示している画面のURLが基準になります。
リソースキーに'app.controller.HogeController'を指定した場合は、以下のようなjsファイルがロードされます。
ここで、HogeController.jsはグローバル空間に'app.controller.HogeController'を公開するコードが記述されている必要があります。例えば以下のように記述されていることを想定しています。
var controller = {
__name: 'app.controller.HogeController',
/* 省略 */
};
// app.controller.HogeControllerにコントローラ定義を公開
h5.core.expose(controller);
})();
以上のように、ディレクトリ構造が名前空間の構造と同じであり、かつ、取得されるjsファイルがグローバルにその名前空間でオブジェクトを公開している必要があります。
h5.settings.res
h5.settings.resにリソース解決時の設定を記述できます。以下、設定項目です。
プロパティ | 型 | 説明 |
---|---|---|
baseUrl | String | リソース取得時に使用するカレントパス。デフォルトはカレントディレクトリ("./")です。 |
コード例
h5.core.controller(document.body, {
__name: 'SampleController',
// 使用されるテンプレートファイルの場所は "/context-root/src/templates/sample.ejs" になる
__templates: h5.res.dependsOn('templates/sample.ejs')
});
使用されるリゾルバ
デフォルトでは、以下のリゾルバが用意されています。
- cssファイル解決
- jsファイル解決
- ejsファイル(テンプレート)解決
- 名前空間(JavaScriptオブジェクト)の解決
どのリゾルバが使用されるかは、それぞれのリゾルバがリソースキーが条件を満たすかどうかで判定しています。上に挙げた順番にリゾルバがリソースキーを判定していき、一番最初にリソースキーが条件を満たすと判定したリゾルバが、リソース解決に使用されます。
各リゾルバの使用判定方法は以下の通りです
- cssファイル解決
リソースキーの末尾が".css"で終わっているかどうか。リクエストパラメータ記述("?○○")を除いて判定します。
- jsファイル解決
リソースキーの末尾が".js"で終わっているかどうか。リクエストパラメータ記述("?○○")を除いて判定します。
- ejsファイル(テンプレート)解決
リソースキーの末尾が".ejs"で終わっているかどうか。リクエストパラメータ記述("?○○")を除いて判定します。
- 名前空間(JavaScriptオブジェクト)の解決
判定条件はなく、単に指定されたリソースキーをオブジェクトパスとみなしてグローバルより探索します。