ES6のクラスとモジュールを使ってMVを実装してみる


jQueryを呪文のように書いていて以来フロントエンドは時が止まった状態だったけど先日ES6を入門した.
モダンJavascriptの技術背景をいくつかキャッチアップしてみたので、まだまだなところはあるけど学習したものを公開します.

# Goal

* ブラウザで動くアプリ
* よくあるフォームに入力した項目がリストに表示されるサンプルを作成する
* gulp, babel, browserifyでES6 => ES5にコンパイルする

# 成果物

![スクリーンショット 2015-07-04 19.50.40.png](https://qiita-image-store.s3.amazonaws.com/0/36978/e81455ac-ca4b-74eb-e8de-9dccc5964769.png "スクリーンショット 2015-07-04 19.50.40.png")


ES5のサンプルコード
https://github.com/metheglin/js-module-challenge/tree/es5-module

ES6のサンプルコード
https://github.com/metheglin/js-module-challenge/tree/es6-module

ディレクトリ構成

![スクリーンショット 2015-07-04 21.09.13.png](https://qiita-image-store.s3.amazonaws.com/0/36978/abe75453-e008-8f69-3d2c-9858668f0e3f.png "スクリーンショット 2015-07-04 21.09.13.png")

# はじめに

モダンjavascript環境を作るには、プロジェクトを作成、直下に移動して`npm init`するとよい.
`package.json`が作成され、ライブラリの依存を管理してくれる.
gulpやgulpのタスクで使われるライブラリは`npm install --save-dev $LIBRARY_NAME`でインストールする.
`-g`オプションをつけずに`npm install`するとカレントディレクトリのnode_modules以下にインストールされる.
`--save-dev`と付与してインストールすると、`package.json`の`devDependencies`に開発用依存ライブラリとして自動で登録される.
`package.json`を保存しておけば、node_modulesを消しても`npm install`すると依存ライブラリを取得することができる.

上のサンプルコードを落とせば`package.json`はコミットしてあるので`npm install`するだけで環境を再現できる.

# gulpタスクについて

```js:gulpfile.js
var gulp        = require("gulp");
var browserify  = require("browserify");
var babelify    = require("babelify");
var source      = require("vinyl-source-stream");

var assets_path     = "public/assets/";
var assets_src_path = "public/assets/src/";

gulp.task("browserify", function(){
  browserify( assets_src_path + "js/main.js")
    .transform( babelify )
    .bundle()
    .on("error", function(err) { console.log("Error: " + err.message); })
    .pipe( source("main.js") )
    .pipe( gulp.dest( assets_path + "/javascripts/" ) )
});

gulp.task("watch", function(){
  gulp.watch( assets_src_path + "js/*.js", ["browserify"] );
});

gulp.task("default", ["browserify", "watch"]);
```

browserifyはブラウザでモジュール記法をサポートするツール. ビルド時にrequire部分を書き換えてブラウザでも動くようになるらしい.
babelifyはbrowserifyでbabelを動かすツール. babelはES6=>ES5変換をする.
vinyl-source-streamはbrowserifyのストリームをvinylオブジェクト(gulp用のオブジェクト?)に変換するツール.

アプリケーションロジックを記述するjavascriptは`public/assets/src/js`以下に配置する. コンパイル対象にするのは`main.js`のみ. `main.js`は依存するモジュールを`require`(ES5)や`import`(ES6)で読み込む.
コンパイルされた`main.js`は`public/assets/javascripts`以下に配置され、htmlの`script`タグで読み込まれる.

# Model Viewについて

フレームワークを使わずに簡単なものをObserverパターンで実装する.

![スクリーンショット 2015-07-05 17.31.05.png](https://qiita-image-store.s3.amazonaws.com/0/36978/d65cdebb-44bc-bf8e-8351-ed2b381ece49.png "スクリーンショット 2015-07-05 17.31.05.png")

```js:Customer.js
import EventObserver from "./EventObserver";

export default class Customer {

  constructor( obj ) {
    this.name = obj.name;
  }

  static add( name ) {
    var customer = new Customer({ name: name });

    Customer.list.push( customer );
    this.trigger( "add", [customer] );
  }
}

Customer.list = [];

$.extend( Customer.prototype, EventObserver.prototype )
$.extend( Customer, EventObserver.prototype )

```

```js:CustomerFormView.js
import Customer from "./Customer";

export default class CustomerFormView {
  constructor( $element ) {
    this.$element = $element;
    this.$input   = this.$element.find( "#input" );
    this.$element.submit( this.onsubmit.bind(this) );
  }

  onsubmit( e ) {
    e.preventDefault();
    Customer.add( this.$input.val() );
  }
}
```

```js:CustomerListView.js
import Customer from "./Customer";

export default class CustomerListView {
  constructor( $element ) {
    this.$element = $element;
    Customer.on( 'add', this.add.bind(this) );
  }

  add( customer ) {
    var item = $('<li>' + customer.name + '</li>');
    this.$element.append( item );
  }
}
```


# References

* http://tsuchikazu.net/javascript-module/
* http://magazine.rubyist.net/?0050-ECMAScript2015
* http://techlife.cookpad.com/entry/2015/02/02/094607
* http://hokaccha.github.io/slides/javascript_design_and_test/