Multipage版のBackbone Yoman Generatorを公開しました

# はじめに
Backbone用のYoman Generatorを公開しました。
https://github.com/cgetc/generator-backbone-multipage
ポイントをざっと説明します。

## 複数ページでのrequire.js
* require.configのbaseUrlを設定しないと、JSの非同期読み込みの際のパスが変わる
* 共通で使用するファイル(common.js)を単一ファイルにまとめた
* require.configをrequire.jsに含めた（HTTPリクエストを減らすため）
* 各ページで使用するファイル(page/*.js)を単一ファイルにまとめた

※以前はrequire.jsをcommon.jsに含めていたが、そうすると、common.jsを読み込むまでブロッキングしてしまうため、require.jsとcommon.jsを切り離した。

## 開発環境と本番環境の切り替え
* require.jsに環境ごとの設定部分をGruntで結合して切り替える。
* 開発環境はminifyしないソースを直接参照する様になっている。

```js:Gruntfile.js
function generate_develop_require_js () {
    var options = _.pick(get_requirejs_options('develop'), ['paths', 'shim', 'urlArgs']);
    options.baseUrl = '/' + jsDir;
    delete options.paths.requirejs;
    grunt.file.write(outJsDir + '/require.js', [
        grunt.file.read('./node_modules/requirejs/require.js'),
        'require.config(' + JSON.stringify(options) + ');'
    ].join('\n'));
}
```

* 本番環境はrequire.jsはminifyされたソースを参照する

```js:Gruntfile.js
function generate_build_require_js () {
    var options = _.pick(get_requirejs_options('build'), ['urlArgs']);
    options.baseUrl = '/' + outJsDir;
    grunt.file.write(jsDir + '/require.js', [
      grunt.file.read('./node_modules/requirejs/require.js'),
      'require.config(' + JSON.stringify(options) + ');'
    ].join('\n'));
}
```

* 本番環境はrequire.jsにmodulesオプションを指定して結合する
* 共通JSはincludeを指定し、各ページのJSはexclueを指定する
* このオプション指定でコード内でrequireされていなくても結合される

```js:Gruntfile.js
grunt.config(['requirejs', 'build', 'options', 'modules'], generate_requirejs_build_modules());
grunt.task.run('requirejs:build');

function generate_requirejs_build_modules () {
    var libs =  _.keys(config.requirejs.paths),
        modules = [{
          name: 'common',
          include: libs
        }];
    grunt.file.recurse(jsDir + '/page', function (abspath, rootdir, subdir, filename) {
      var dir = subdir? subdir + '/' : '';
      modules.push({
        name: 'page/' + dir + filename.slice(0, -3),
        exclude: libs
      });
    });
    return modules;
}
```

## HTMLからrequire.jsモジュールへの変数の渡し方
* 各ページのJSをFunctionのモジュールとして定義する

```js:page/todo.js
define(['backbone', 'collection/Todos', 'view/todo/ListView'], function (Backbone, Todos, ListView) {
	return function (todo_list) {
		var todos = new Todos();
		view = new ListView(todos);
		todos.set(todo_list);
		new Router();
		Backbone.history.start();
	};
});
```

* 各ページのJSをrequireし、引数としてJSONを渡す


```html:inex.html
<script>
	require(['page/todo'], function (load) {
		load([
			{id: 1, title: 'hoge', description: 'hogehogehoge'},
			{id: 2, title: 'fuga', description: 'fugafugafuga'}
		]);
	});
</script>
```

## テンプレート管理
* JSTテンプレートをgrunt-contrib-jstでJSにプリコンパイルする
* {namespace: false, amd: true}をオプション指定するとグローバル変数なしのAMD形式で出力される
* デフォルトではwith文を使うためtemplateSettings:variableオプション指定して抑制する

```js:Gruntfile.js
grunt.initConfig({
    jst: {
      options: {
        namespace: false,
        amd: true,
        templateSettings: {
          variable: 'data'
        }
      }
    }
});
```
```html:tmpl/todo/ListView.ejs
<li>
	<a href="#description/<%= data.id %>"><%= data.title %></a>
</li>
```
* JSファイルなのでrequire.jsで読み込める

```js:ListView.js
define(['backbone', 'tmpl/todo/ListView'], function (Backbone, listTmpl) {
	return Backbone.View.extend({
		el: '#todo-list',
		template: listTmpl,
	});
});
```

# 雑感
* Backbone.jsは本来SPA（シングルページアプリケーション）を作成するものだが、規模が大きくなると分割した方がよいと考え、複数ページのベストプラクティスを模索した。
* require.jsの設定部分をGrunt.js内で書き出す以外に方法はないものか？(SPA前提であれば、data-mainで読み込めばいい)
* HTMLからrequire.jsモジュールへの変数の渡し方は他にないものか？(scriptタグ内にBackbone.jsのマウントポイントを置くよりはこちらの方が好み。)
* Backbone.jsにはtextプラグインがあるが、JSTをプリコンパイルした方が、パフォーマンス上優位なのは当然だが、テンプレートのフォルダもjsフォルダ配下に置かなくて済むのもメリット。
* テンプレートファイルの読み込みもrequire.jsで都度結合できるようになるメリットもある。

# その他
* [Compassのsourcemap出力](http://qiita.com/cgetc@github/items/34299ea71bd2edd1711c)