作って壊せるReact開発環境をつくる(gulp + browserify + babel)

社内 React(flux)勉強会のために、シンプルなReact開発環境を作ったので公開します。
https://github.com/kjugk/flux_boilerplate

基本的な部分をこの記事で解説しますので、これからReact始めたいけど設定が面倒くさそう・・という方は是非お試しください!（フィードバック大歓迎です。）

## 前提
* node v5.3.0 で動作確認済

## ディレクトリ構成
ディレクトリ構成は以下になります。(とりあえずReact に慣れることが目的なので、test や css は省略しています。)

2016/03/27 追記
JestでReact Component をテストする記事を作成しましたので、合わせてご参照ください!
http://qiita.com/kjugk/items/d0306eb2a1ff97a07d6f
追記ここまで

```
.
├── .babelrc
├── dist         //browserify でコンパイルされた bundle.js ファイルを配置する。
├── gulpfile.js
├── index.html
├── js           // JavaScript ファイルを配置する
│    └── index.js
│    └── 〜
│  
├── node_modules
└── package.json

```

## ビルドのフロー
![Untitled (1).png](https://qiita-image-store.s3.amazonaws.com/0/46222/32552c46-6a9b-290b-704f-54fa8f69def8.png)


##各ファイルの説明

#### gulpfile.js
gulp task を記述します。
jsタスクでは、js ディレクトリ配下のファイルを browserify で bundle.js ファイルにまとめて、dist ディレクトリに配置しています。

```:gulpfile.js
''use strict';
var gulp = require('gulp');
var gutil = require('gulp-util');

var browserify = require('browserify');
var watchify = require('watchify');
var babelify = require('babelify');

var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var sourcemaps = require('gulp-sourcemaps');

var b = browserify({
  entries: ['./js/index.js'],
  transform: ['babelify'],
  cache: {},
  packageCache: {},
  plugin: [watchify]
})
.on('update', bundle)
.on('log', gutil.log)

function bundle(){
  return b.bundle()
    .on('error', gutil.log.bind(gutil, 'Browserify Error')  )
    .pipe(source('bundle.js'))
    .pipe(buffer())
    .pipe(sourcemaps.init({loadMaps: true}))
    .pipe(sourcemaps.write('./'))
    .pipe(gulp.dest('./dist'));
}

gulp.task('js', bundle);
gulp.task('default', ['js']);
```
ここで重要なのは、browserify を設定している以下の記述です。

```:gulpfile.js
var b = browserify({
  entries: ['./js/index.js'],
  transform: ['babelify'],
  cache: {},
  packageCache: {},
  plugin: [watchify]
})
.on('update', bundle) //ソース更新時に bundle を再実行
.on('log', gutil.log) //標準出力にログを書き出す
```
* transform に babelify を指定しています。b.bundle 関数実行時に、babelify によるES6, JSX のtranspile が実行されます。
* plugin に watchify を指定しています。b.bundle 関数実行時に watchify によるソースの監視が始まり、ソースを更新するたびに差分build が実行されます。 

#### .babelrc
* babelify モジュール の設定はここに記述します。

```:.babel.rc
{
  "presets": ["es2015", "react"],
  "plugins": ["transform-object-assign"]
}
```
* ES6 と JSX をコンパイルするために、[es2015](https://babeljs.io/docs/plugins/preset-es2015/) と [react](http://babeljs.io/docs/plugins/preset-react/)  presets(基本的なpluginのセット) を定義しています。
* React 開発でよく利用する、Object.assign() を有効にするため、[transform-object-assign](https://babeljs.io/docs/plugins/transform-object-assign/) plugin を定義しています。

* その他の babel の plugin は [こちら](https://babeljs.io/docs/plugins/)

#### index.html
では、以下のように、script タグで コンパイル済みのbundle.js を読み込んでいるだけです。

```:index.html
~
<body>
  <div id="root"></div>
  <script src="dist/bundle.js"></script>
</body>
~
```

#### index.js
browserify のエントリーポイントになるファイルです。
以下のように、index.html の DOM に React Component をマウントしています。
前述のbabelify の 設定により、以下全ての js ファイルで、import 構文や JSX の記述が使えます。

```:index.js
import React from 'react'
import {render} from 'react-dom'
import ExampleApp from './components/ExampleApp'

render(
  <ExampleApp />,
  document.getElementById('root')
)
```

## build
以上の設定により、コンソール から gulp コマンドを叩くと、ソースのコンパイル + 監視が始まります。(global に gulp をインストールしている場合は、gulp コマンドでOKです。)

```
$ node ./node_modules/gulp/bin/gulp
[15:31:10] Using gulpfile ~/flux_boilerplate/gulpfile.js
[15:31:10] Starting 'js'...
[15:31:12] 690771 bytes written (2.48 seconds)
```
buildが失敗すると、以下のようなエラー(一部dir name改変)がコンソールに吐き出されます。

```
[15:36:01] Browserify Error { [SyntaxError: /xxx/flux_boilerplate/js/components/ExampleApp.js: Adjacent JSX elements must be wrapped in an enclosing tag (33:6) while parsing file: /xxx/flux_boilerplate/js/components/ExampleApp.js]
  pos: 675,
  loc: Position { line: 33, column: 6 },
  _babel: true,
```
どうやら Example.js ファイルの33行目のJSX が、タグで閉じられていないことが原因のようです。

修正してファイルを更新すれば、自動的にコンパイルが始まりるので、エラーが出なくなるまでコードを修正していきます。


## 動作確認
ブラウザで index.html を開くと、bundle.js が実行されます。

---

以上長々と説明してきましたが、ここまでできればあとは作って壊して、React に慣れるだけです！
ちなみに、Redux などを導入したい場合でも、基本的に上記の設定を流用できますので、試してみてください。

## 今後の課題
* error メッセージをもっと綺麗に出力したい。
* test を書く。=> http://qiita.com/kjugk/items/d0306eb2a1ff97a07d6f
* production 用のbuild task を定義する。
* rails と 組み合わせるbest practiceを探す。

## 参考にさせていただいた記事
[React.js + Babel + Browserify + gulp の環境を作ってみた](http://qiita.com/hkusu/items/e068bba0ae036b447754)