Rails5+webpacker+Reactを試してみた


webpacker
https://github.com/rails/webpacker


確認バージョン
-------------

- Ruby 2.3.3
- Rails 5.0.1
- webpacker 確認時点でのhash:215cdbc5858309de85d71932aa3468bb76feb4a2



Railsアプリ作成
---------------

適当に新規Railsアプリを作る

```
$ mkdir webpack-sandbox
$ cd webpack-sandbox
$ rails new -T .
```

Gemfileを変更(追加)
(GitHub指定が無いと `rake webpacker:install:react` が使えない)

```Gemfile
gem 'webpacker', github: 'rails/webpacker'
```

インストール & 確認

```
$ bundle install
$ bundle exec webpack -h
```
```
webpack 1.14.0
Usage: https://webpack.github.io/docs/cli.html

Options:
  --help, -h, -?
  --config
  --context
  ...
```


テンプレートを生成する
----------------------

```
$ bundle exec bin/rails webpacker:install
```
```
      create  app/javascript
      create  app/javascript/packs/application.js
       exist  bin
      create  bin/webpack-dev-server
      create  bin/webpack-watcher
      create  bin/webpack
      create  bin/yarn
      create  config/webpack
      create  config/webpack/development.js
      create  config/webpack/production.js
      create  config/webpack/shared.js
      append  .gitignore
         run  ./bin/yarn add --dev webpack@beta webpack-merge webpack-dev-server@beta path-complete-extname babel-loader babel-core babel-preset-latest coffee-loader coffee-script rails-erb-loader from "."
yarn add v0.17.10
info No lockfile found.

...
```

生成後の構成（特徴的なもののみ抜粋)

```
.
├── Gemfile
├── Gemfile.lock
├── README.md
├── Rakefile
├── app
│   └── javascript
│       └── packs
│           └── application.js
├── bin
│   ├── webpack
│   ├── webpack-dev-server
│   ├── webpack-watcher
│   └── yarn
├── config
│   └── webpack
│       ├── development.js
│       ├── production.js
│       └── shared.js
└── vendor
    ├── assets
    │   └── javascripts
    ├── package.json
    └── yarn.lock
```


- package.jsonはvendor配下に配備される
- shared.js → development.js / production.js 両方から参照される


ビルドする
------------


```
$ bundle exec bin/webpack
```
```
Hash: 071bb7221801f498ad46
Version: webpack 2.2.0-rc.4
Time: 482ms
             Asset     Size  Chunks             Chunk Names
    application.js  3.37 kB       0  [emitted]  application
application.js.map  3.34 kB       0  [emitted]  application
   [0] ../app/javascript/packs/application.js 496 bytes {0} [built]
```


Viewから読み込む
-----------------

### jsの読み込みを変更


```application.html.erb
<%= javascript_pack_tag 'application' %>
```



起動してみる
----------

### 適当にコントローラを作成

```
$ bundle exec rails g controller Sample index
```

```routes.rb
root to: 'sample#index'
```

起動

```
$ bundle exec rails s
```

上手くいけばコンソールにログが出力される

![Kobito.sRykAI.png](https://qiita-image-store.s3.amazonaws.com/0/55375/0476b619-a6f8-ac26-7cef-a6a15a936d36.png "Kobito.sRykAI.png")


### ダイジェスト値の付与を確認(production起動)

ただこの場合、development環境のため、jsにダイジェスト値が付与されていない。
productionでダイジェストが付与されることを確認してみる。


#### SERCRET_KEY_BASEを設定

```
$ bundle exec rake secret
```

出力されたhash値を設定


```
$ export SECRET_KEY_BASE={hash}
```

digestファイルを生成

```
$ bundle exec rake webpacker:compile
```

#### production用ビルド & 起動

```
$ RAILS_ENV=production bundle exec bin/webpack
$ export RAILS_SERVE_STATIC_FILES=true
$ RAILS_ENV=production bundle exec rails s
```


![Kobito.aY8GUA.png](https://qiita-image-store.s3.amazonaws.com/0/55375/1d7d3462-c571-d824-13e3-c125dc019351.png "Kobito.aY8GUA.png")


ダイジェストが付与されているスクリプトがロードされてますね。

Reactを使う
==========

```
$ bundle exec bin/rails webpacker:install:react
```

```application.html.erb
<%= javascript_pack_tag 'hello_react' %>
```

```
$ bundle exec bin/webpack
$ bundle exec rails s
```

![Kobito.b7Y3B9.png](https://qiita-image-store.s3.amazonaws.com/0/55375/31a782f3-5e18-5caa-cb45-ab34848129eb.png "Kobito.b7Y3B9.png")

デフォルトで生成されるhello_react.jsのHelloコンポーネントが出力されている。

コンポーネントを追加してみる
------------------------

適当にコントローラを追加

```
$ bundle exec rails g controller Count index
```

衝突しないようにデフォルトのスクリプトを外す

```application.html.erb
<head>
  <title>WebpackerSandbox</title>
  <%= csrf_meta_tags %>
</head>
```

#### スクリプト作成

import/exportの挙動確認の意味も込めて無意味にネスト

```/app/javascript/packs
.
├── counter
│   ├── counter.js
│   └── index.js
└── counter.js
```

```/app/javascript/packs/counter.js
import React from 'react'
import ReactDOM from 'react-dom'
import * as counter from './counter/index'

document.addEventListener("DOMContentLoaded", e => {
  ReactDOM.render(<counter.Counter />, document.body.appendChild(document.createElement('div')))
})
```

```/app/javascript/packs/counter/index.js
export * from './counter'
```

```/app/javascript/packs/counter/counter.js
import React from 'react'

export class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: props.count || 0
    }
  }

  handleClick() {
    this.setState({
      count: this.state.count + 1
    })
  }

  render() {
    return (
      <div>
        <div>COUNT: {this.state.count}</div>
        <div><input type="button" value="COUNT UP" onClick={() => this.handleClick()} /></div>
      </div>
    )
  }
}
```


#### ビルドして動作確認

スクリプトのロードを末尾に追加

```/app/views/count/index.html.erb
<%= javascript_pack_tag 'counter' %>
```

webpackビルドや起動方法などは先ほどと同じ。

![caps.gif](https://qiita-image-store.s3.amazonaws.com/0/55375/f2ba2d1f-203d-9f33-fb19-547483a73743.gif "caps.gif")


なんか動いてるっぽい。


所感
===

今までwebpack絡めた環境作るのが手間に感じていたのが、webpacker使うとかなり楽に感じました。

いままではwebpackでビルド→ビルド生成物をasset pipelineに乗せてダイジェスト付与の恩恵だけ受ける、みたいなことをやってたんですが、そのあたりも含めて面倒見てくれるのは嬉しく思いました。

Rails 5.1に期待。