RequireJS moduleについて
RequireJSって何?
公式サイト
RequireJS
スライド
jQueryRequireJS.pdf
日本語記事だとこの辺?
http://zudolab.net/blog/?p=451
要はJavaScriptの依存性解決をしてくれるライブラリで、こんな感じで使えます。
require( [ 'lib/a' ,'lib/b' ,'lib/c' ], function(){ // lib/a.js,lib/b.js,lib/c.jsが読み込まれていることが保証されているcallback require( [ 'lib/d' // lib/a.jsに依存しているライブラリ ,'lib/f'// lib/b.jsに依存しているライブラリ ], function(){ // lib/a.js,lib/b.js,lib/c.js,lib/d.js,lib/e.jsが読み込まれていることが保証されているcallback }); } );
callbackの中でならライブラリが読み込まれてる前提でアプリケーションコードが書けるし、
callbackの中でrequire関数を呼べば、あるライブラリに依存する別のライブラリに依存する
アプリケーションとかも書けちゃいますよ、という感じ。
と言っても、これはかなり単純な使い方で、アプリケーションコードの規模が
小さい場合でないと実用的ではないと思う。
RequireJS moduleって何?
そこで、moduleですよ。
define({})
例えば、こんな内容のhoge.jsが配置されているとする。
このhoge.jsがhoge module。
define({ hoge : 5 ,fuga : 'piyo' });
requireの第一引数に依存しているmodule名の配列を渡すと、
その順にcallback関数に渡される。
require(['hoge'],function(hoge){ console.log(hoge.hoge); // 5 console.log(hoge.fuga); // 'piyo' });
define(function(){})
moduleはdefine関数にオブジェクトを渡すだけでなく、
オブジェクトを返す関数を渡す形でも定義できる。
これはfoo.js(foo module)
define(function(){ return { bar : 6 ,baz : 7 } });
先程のhoge moduleとあわせて使うと
require(['hoge','foo'],function(hoge,foo){ console.log(foo.bar); // 6 console.log(hoge.fuga); // 'piyo' });
依存関係の連鎖
RequireJS moduleではdefine関数の第一引数にmodule名の配列を渡すことで、
moduleが依存するmoduleを指定することができる。
hage.js(hage module)
define(['hoge','foo'],function(hoge,foo){ return { bar : foo.bar ,baz : foo.baz ,hoge : hoge.hoge ,fuga : hoge.fuga } });
下記のrequire(['hage'])ではhage moduleしか指定していないが、
依存先のhoge,fooも読み込まれている。
hage moduleを使いたい人はhage moduleが何に依存しているか知っている必要はない。
require(['hage'],function(hage){ console.log(hage.bar); // 6 console.log(hage.fuga); // 'piyo' })
require(['hage'])を呼んだ場合、以下のような順で処理が実行される。
- hage.jsが読み込まれる
- hoge.jsが読み込まれる(hoge moduleが定義される)
- foo.jsが読み込まれる
- foo.jsでdefine関数に渡しているcallbackが実行され、戻り値がfoo moduleとして定義される
- hage.jsでdefine関数に渡しているcallbackが(hoge,foo moduleを使用して)実行され、戻り値がhage moduleとして定義される
- require(['hage'])のcallbackにhage moduleが渡され呼び出される
define関数のcallbackはmoduleの初期化用であって、
そのmoduleが依存moduleとして初めて呼ばれた時だけ実行される。
この依存関係の連鎖は何段階でもできる。
関数・class
今までの例ではmoduleとして連想配列しか定義していなかったが、
JavaScriptで扱えるすべての値がmoduleとして定義できる。
例えばこんなvery-useful-function.jsがあれば、
define(function(){ function veryUsefulFunction(){ /* * 何かすごい実装 */ } return veryUsefulFunction; });
これを使いたい人は、
require(['very-useful-function'],function(veryUsefulFunction){ /* * 何か自前のコード */ veryUsefulFunction(); });
とか
define(['very-useful-function'],function(fun){ /* * 何か自前のコード */ fun(); return module; });
とか書いてやればいいし、
こんなvery-useful-class.jsがあれば、
define(function(){ function VeryUsefulClass(){ } VeryUsefulClass.prototype.benri = function(){ /* * 何かすごい実装 */ }; return VeryUsefulClass; });
こんな風に書いてやればいい
require(['very-useful-class'],function(Useful){ var hogehoge = new Useful(); hogehoge.benri(); });
それぞれRequireJSのmoduleとして関数やclassを提供しているが、
グローバルは全く汚染していない。
まとめ
依存関係の連鎖と関数・classの提供などを組み合わせて、
アプリケーションコードを小分けにしていけば、
JavaScriptアプリケーションのコード規模が大きくなっても
スパゲッティ化を逃れられるのではないかと期待している。
もそっと深いところを把握した上で、java-ja.js#2で発表しようと思う。