AMD & RequireJS

このドキュメントではAMDおよびRequireJSについて纏めています。

追記：
もしあなたが新規にJavaScriptで開発する予定なのであれば、先にまとめを読んだほうが幸せになれるかもしれません。


# What is RequireJS?
RequireJS (http://requirejs.org/) とは、モジュール化したjavascriptを良しなに読み込んでくれるフレームワークです。

require.jsは公式サイトからダウンロードできます。
http://requirejs.org/docs/download.html#latest

※ダウンロードページには require.js と r.js というものがありますが、
require.js はモジュールを読み込むためのものです。ブラウザで読み込んで利用します。
r.js はモジュールを最適化するためのビルドツールです。ブラウザでは利用しません。


# Why Module?
モジュール化する最たるメリットは依存関係の明瞭化です。
従来は依存関係を考慮し読み込ませる順序を手動で決める必要があった為、プロジェクトが拡大するほど実装が難解になりがちでした。

```html:index.html
<script type="text/javascript" src="js/melancholy/haruhi.js"></script>
<script type="text/javascript" src="js/puella/madoka.js"></script>
<script type="text/javascript" src="js/melancholy/kyon.js"></script>
<script type="text/javascript" src="js/puella/homura.js"></script>
<script type="text/javascript" src="js/melancholy/nagato.js"></script>
<script type="text/javascript" src="js/puella/sayaka.js"></script>
<script type="text/javascript" src="js/melancholy/mikuru.js"></script>
<script type="text/javascript" src="js/puella/mami.js"></script>
<script type="text/javascript" src="js/melancholy/koizumi.js"></script>
<script type="text/javascript" src="js/puella/kyouko.js"></script>
<script type="text/javascript" src="js/app.js"></script>
```

( ◠‿◠ )☛ 新規ファイルはどのへんに追加しようかな
▂▅▇█▓▒░(’ω’)░▒▓█▇▅▂うわああああああああ

辛いですね。
モジュール化(AMD対応)すると、先ほどのコードをこのように書き換えることができます。

```html:index.html
<script data-main="app.js" src="require.js"></script>
```

それぞれのモジュール自身が、どのモジュールに依存しているか記述しているため、
RequireJSに起点となるモジュールを指定すれば、必要なモジュールを自動で読み込んでくれます。
従って新規にファイルを追加する際に、scriptタグを書き加える必要がなくなります。


# What is AMD(Module API)?
javascriptのモジュール定義に関連するAPIはいくつかあります。

- CommonJS (http://wiki.commonjs.org/wiki/Modules/1.1.1)
- AMD (http://requirejs.org/docs/whyamd.html)
- ES2015 Module (http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts)
- ...

これらは全てモジュールの依存関係を解決するために設計されたものです。
基本的な点は一緒ですが、 CommonJS は当初ブラウザ環境で実行されることが想定されていなかったため、ブラウザ環境での実行を考慮し、依存関係の解決および遅延ロードに対応したのが AMD です。
またES2015の仕様が策定されたことにより、新しいJavaScriptでは標準でモジュール化が可能となりました。

### AMD
`define()`に配列でモジュール名を指定する記法です。
RequireJSは、この形式(AMD)で記述したモジュールを読み込むことができます。

```js:amd.js
define(['jquery', 'underscore'], function ($, _) {
    function a(){}; // public
    function b(){}; // private
    return a;
});
```

### CommonJS
`require()`にモジュール名を指定する記法です。
こちらは RequireJS ではなく WebPack や Browserify で読み込むことが出来ます。

```js:commonjs.js
var $ = require('jquery');
var _ = require('underscore');
function a(){}; // public
function b(){}; // private
module.exports = a;
```

# How to define a module?
RequireJS(AMD)のモジュール定義の具体的な書き方についてです。
Docs: http://requirejs.org/docs/api.html#define

### Definition Functions
シンプルに関数をモジュール化するならばこのように実装します。

```js
define(function(){
    return function(){
        // code
    }
});
```

### Definition Functions with Dependencies
依存関係のある関数をモジュール化するならばこのように実装します。

```js
define(["hoge", "piyo"], function(hoge, piyo) {
    return function(){
        // code
    };
});
```

基本的には上記のような実装で問題ないのですが、依存モジュールが多くなってくると以下の様なコードになってしまいます。

```js
define(["foo", "bar", "baz", "qux", "quux",
        "corge", "grault", "garply", "waldo",
        "fred", "plugh", "xyzzy", "thud"],
function (foo, bar, baz, qux, quux,
          corge, grault, garply, waldo,
          fred, plugh, xyzzy, thud) {
    return function(){
        // code
    }
});
```

うげえええ、これは嫌ですね。

### Define a Module with Simplified CommonJS Wrapper
そこで RequireJS は CommonJS に似た記述 (Simplified CommonJS Wrapper) が可能となっています。

```js
define(function(require, exports, module) {
    var foo = require('foo');
    var bar = require('bar');
    var baz = require('baz');
    var qux = require('qux');
    var quux = require('quux');
    var corge = require('corge');
    var grault = require('grault');
    var garply = require('garply');
    var waldo = require('waldo');
    var fred = require('fred');
    var plugh = require('plugh');
    var xyzzy = require('xyzzy');
    var thud = require('thud');

    return function(){
        // code
    };
});
```

好みがあるかもしれませんが、こちらのほうが可読性が高いように見えます。

# How to load a first module?
モジュールを読み込むには、起点となるモジュールをRequireJSに指定する必要があります。
RequireJSは指定されたモジュールを読み込み、それに依存するモジュール、さらに依存するモジュールを次々と(重複なく)読み込んでいきます。
起点となるモジュールを指定する方法はいくつかあります。(設定ファイルについては後述します)
Docs: http://requirejs.org/docs/api.html#jsfiles

### data-main
requirejsを読み込むscriptタグにdata-main要素を追加する方法です。
普通はこの方法で読み込むのが一般的なようです。

```html:index.html
<script src="require_config.js"></script>
<script data-main="app.js" src="require.js"></script>
```

### callback
設定ファイル内で起点となるファイルをcallbackする方法です。

```html:index.html
<script src="require_config.js"></script>
<script src="require.js"></script>
```
```js:require_config.js
var require = {
    deps: ["app"],
    callback: function(app){}
};
```

### require
require() 関数でモジュールを読み込む方法です。

```html:index.html
<script src="require_config.js"></script>
<script src="require.js"></script>
<script>
    require(['app'], function(app){});
</script>
```
```js:require_config.js
var require = {
    deps: ["app"]
};
```


# How to write a config file?
RequireJSを利用する際に、各種オプションを設定ファイルに記述することができます。
よく使われるのが`baseUrl`, `paths`, `shim`といったオプションです。
他にも設定可能なオプションがあるので、詳しくは公式ドキュメントを参照してください。
Docs: http://requirejs.org/docs/api.html#config

### baseUrl
baseUrlオプションでは、デフォルトのパスを指定することができます。

```js
var require = {
  baseUrl: 'js',
  paths: {
    hoge: 'aaa/bbb/hoge'
  }
};
```

この例の場合、`hoge`のパスは`js/aaa/bbb/hoge`となります。
注意として、最初に読み込むモジュールをdata-main要素で指定した場合は、data-main要素で指定したパスがbaseUrlになります。

### paths
pathsオプションでは、モジュールのパスを指定することができます。(拡張子は不要です)
baseUrlで指定したパス以外のパスにあるモジュールも個別に設定することができます。

```js
var require = {
  baseUrl: 'js',
  paths: {
    hoge: 'aaa/bbb/hoge',           // js/aaa/bbb/hoge.js
    piyo: '../ccc/ddd/piyo',        // ccc/ddd/piyo.js
    fuga: 'http://example.com/fuga' // http://example.com/fuga.js
  }
};
```

`hoge`は`baseUrl+paths`が参照されます。
`piyo`は`baseUrl`からの相対パスが参照されます。
`fuga`はURL先が参照されます。

### shim
shimオプションでは、AMDに対応していないjavascriptを指定できます。

```js
requirejs.config({
    shim: {
        'backbone': {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        },
        'jquery.colorize': ['jquery'] // exportsを省略する記法
    }
});
```

この例では`backbone`は`underscore`と`jquery`より後に読み込まれるように指定しています。
もう一つも同様に`jquery.colorize`は`jquery`より後に読み込まれるように指定しています。


# How to load a config file?

RequireJSの設定ファイルの読み込み方についてです。
設定は最初に読み込むモジュール内の最初に実装するか、別ファイルに実装してrequirejsより前に読み込みます。

### Inner module
起点となるモジュール内に実装する場合は以下のようになります。

```html:index.html
<script data-main="app.js" src="require.js"></script>
```
```js:app.js
// config
require.config({
    paths: {
        hoge: 'hoge'
    }
});
 
// app
require(['hoge'], function(hoge) {
    // code
});
```

### Outer module
設定ファイルを分離して実装する場合は以下のようになります。

```html:index.html
<script src="require_config.js"></script>
<script data-main="app.js" src="require.js"></script>
```
```js:require_config.js
var require = {
    paths: {
        hoge: 'hoge'
    }
});
```

```js:app.js
require(['hoge'], function(hoge) {
    // code
});
```

# How to build? (r.js)
RequireJSは読み込むモジュールを1つないしは複数のファイルにビルドすることができます。
Docs: http://requirejs.org/docs/optimization.html

### Install
npm で r.js をインストールします。
(requirejsと入力していますが、インストールされるのはr.jsです。紛らわしいですね。)

```shell-session:shell
npm install requirejs
```

### Use
node で r.js を実行します。
r.js の後には`-o`オプションでビルド設定を記述します。
また`optimize=none`を指定すると、結果が最適化されずに出力されます。

```shell-session:shell
node r.js -o baseUrl=. paths.jquery=some/other/jquery name=main out=main-built.js
```

設定は直接入力してもよいですが、ファイルを指定することも可能です。

```shell-session:shell
node r.js -o build.js
```

```js:build.js
({
    baseUrl: ".",
    paths: {
        jquery: "some/other/jquery"
    },
    name: "main",
    out: "main-built.js"
})
```

ビルドに成功すると複数のモジュールがひとつのjavascriptファイルになります。
設定によっては複数のファイルに分割して出力することも可能です。

```
/js/puella.js
----------------
/js/puella/madoka.js
/js/puella/homura.js
/js/puella/sayaka.js
/js/puella/mami.js
/js/puella/kyouko.js

/js/melancholy.js
----------------
/js/melancholy/haruhi.js
/js/melancholy/kyon.js
/js/melancholy/nagato.js
/js/melancholy/mikuru.js
/js/melancholy/koizumi.js

```

ビルド設定の書き方については公式ドキュメントの設定例が詳しいので確認して下さい。
(オプションが多々あり把握できていないので、ここでの説明は省略します、申し訳ない。。。)
https://github.com/jrburke/r.js/blob/master/build/example.build.js

またr.jsでjavascriptファイルを一つに纏める際、require.jsも一緒に纏めることができます。
その際、requireは予約語みたいなものなので、requireLibなどに名称を変えてincludeします。
http://requirejs.org/docs/optimization.html#onejs


# まとめ
本記事ではRequireJSの役割や使い方について説明しました。

既存のJavaScriptファイルをAMDでモジュール定義し、require.jsを読み込むことによって、必要なモジュールの読み込みや依存関係を解決することができるようになりました。

また実際のWebサイトを運用する上では、リクエスト数を削減するために一つのJavaScriptファイルに結合して配布する場合がほとんどですから、r.jsなどを利用して事前にビルドするのが良いでしょう。

しかしJavaScriptをビルドする前提なのであれば、最近はAMDではなくCommonJSあるいはES2015でモジュール定義し、WebPack あるいは Browserify でビルドするほうが主流となっています。

RequireJSの利点は、require.jsをブラウザで読みこめばビルド不要でモジュールの恩恵が受けられる点ですので、ビルドをしない前提であればRequireJSは現在でも覚えておいて損はないでしょう。
しかし特段の理由がないのであれば、前述の通り一つのファイルに結合するなどして配布するほうが良いはずですので、最近はRequireJS(AMD)を採用する機会は少ないのではないかと思います。

従って、もしJavaScriptのモジュール化を検討しているのであれば、合わせてWebPackやBrowserifyなどのビルドツールや、ES2015で策定されたimport文やCommonJSなどについても調べることをおすすめいたします。

拙い文章でしたが、最後までご覧頂きありがとうございました！！