Rails5.1から追加されると噂のWebpackerを使ってReact.jsを動かす。

# 概要

Rails5.1では `webpack` が導入され、さらに `React` がデフォルトで使われるのでは！と噂される今日この頃。
Railsのプロジェクト内にある `webpacker` を試しに使ってみました。
さらに、この `webpacker` を使うと `React` も簡単に使えたので、そちらも試してみました。



# 環境
| 項目 | 説明 |
|----|----|
| OS | OS X El Capitan |
| パッケージ管理 | Homebrew |
| その他 | Ruby 2.3, Rails 5.0.1 |


# サンプル環境の準備

まずは `webpacker` で使用する `yarn` （javascriptのバージョン管理ソフト）をインストールする。

```bash
$ brew install yarn
```

Railsプロジェクトを作成する。

```bash
$ rails new webpacker-sample && cd webpacker-sample
```


# Webpacker をインストール

`Gemfile` に下記を追加する。

```rb:Gemfile
...
gem 'webpacker', github: "rails/webpacker"

```

gemをインストールする。

```bash
$ bundle
```

インストール後、コマンドが追加されていることを確認する。

```bash
$ rake -T | grep webpack

# 以下出力結果
rake webpacker                          # Lists available tasks under webpacker
rake webpacker:compile                  # Compile javascript packs using webpack for production with digests
rake webpacker:install                  # Install webpacker in this application
rake webpacker:install:angular          # Install everything needed for Angular
rake webpacker:install:react            # Install everything needed for react
rake webpacker:install:vue              # Install everything needed for Vue

```

webpackを使う準備（インストール）をする。

```bash
$ rake 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のパッケージインストールのログが出力されますが、長いので省略。

```

## 動作確認

サンプルページの作成を作成し、webpackでbuildして表示確認をする。

まずは、サンプルページを作成。

```bash
$ rails g controller pages index

# 以下出力結果
Running via Spring preloader in process 85513
Expected string default value for '--jbuilder'; got true (boolean)
Expected string default value for '--helper'; got true (boolean)
Expected string default value for '--assets'; got true (boolean)
      create  app/controllers/pages_controller.rb
       route  get 'pages/index'
      invoke  erb
      create    app/views/pages
      create    app/views/pages/index.html.erb
      invoke  test_unit
      create    test/controllers/pages_controller_test.rb
      invoke  helper
      create    app/helpers/pages_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/pages.coffee
      invoke    scss
      create      app/assets/stylesheets/pages.scss

```

`app/views/layouts/application.html.erb` の `javascript_include_tag` を `javascript_pack_tag` というヘルパーメソッドに変更する。

```ruby:app/views/layouts/application.html.erb

<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

```


webpackでbuildする。

```bash
$ bin/webpack

# 以下出力結果
Hash: 071bb7221801f498ad46
Version: webpack 2.2.0
Time: 1771ms
             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]

```

Railsサーバ起動後

```bash
$ rails s
```
起動が確認されたら `http://localhost:3000/pages/index` にアクセスする。 
下記のような画面に右側のコンソールに `Hello World from Webpacker` と出力されていればコンパイルされ無事に読み込まれています。


<img width="888" alt="スクリーンショット 2017-01-18 20.39.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/29570/4379a1fb-4144-b5a1-8f88-480c393cb606.png">
（buildされる `/app/javascript/packs/application.js`には `console.log("Hello World from Webpacker")` と記述してあります。）

あとは、 `app/javascript/packs/application.js` 内にjavascriptを記述していけばOKです。


# Reactを動かす

Reactをインストール

```bash
$ rake webpacker:install:react

# 以下出力結果
Replacing loader presets to include react in /Users/yoshi/works/ruby/local/webpacker/wp-sample/config/webpack/shared.js
Replacing loader test to include react in /Users/yoshi/works/ruby/local/webpacker/wp-sample/config/webpack/shared.js
Copying react example to app/javascript/packs/hello_react.js
yarn add v0.19.1
warning No license field
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 📃  Building fresh packages...
success Saved lockfile.
success Saved 10 new dependencies.
├─ babel-helper-builder-react-jsx@6.21.1
├─ babel-plugin-syntax-flow@6.18.0
├─ babel-plugin-syntax-jsx@6.18.0
├─ babel-plugin-transform-flow-strip-types@6.21.0
├─ babel-plugin-transform-react-display-name@6.8.0
├─ babel-plugin-transform-react-jsx-self@6.11.0
├─ babel-plugin-transform-react-jsx-source@6.9.0
├─ babel-plugin-transform-react-jsx@6.8.0
├─ babel-preset-react@6.16.0
└─ esutils@2.0.2
warning No license field
✨  Done in 8.11s.
yarn add v0.19.1
warning No license field
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
warning "react-dom@15.4.2" has unmet peer dependency "react@^15.4.2".
[4/4] 📃  Building fresh packages...
success Saved lockfile.
success Saved 15 new dependencies.
├─ asap@2.0.5
├─ core-js@1.2.7
├─ encoding@0.1.12
├─ fbjs@0.8.8
├─ iconv-lite@0.4.15
├─ is-stream@1.1.0
├─ isomorphic-fetch@2.2.1
├─ loose-envify@1.3.1
├─ node-fetch@1.6.3
├─ promise@7.1.1
├─ react-dom@15.4.2
├─ react@15.4.2
├─ setimmediate@1.0.5
├─ ua-parser-js@0.7.12
└─ whatwg-fetch@2.0.1
warning No license field
✨  Done in 16.58s.

```

`React`のインストールで以下のようなサンプルファイルが生成される。

```js:app/javascript/packs/hello_react.jsx
import React from 'react'
import ReactDOM from 'react-dom'

class Hello extends React.Component {
  render() {
    return <div>Hello {this.props.name}!</div>
  }
}
document.addEventListener("DOMContentLoaded", e => {
  ReactDOM.render(<Hello name="React" />, document.body.appendChild(document.createElement('div')))
})

```


生成されたサンプルファイル `hello_react.js` を読み込むように変更をする。

```js:app/javascript/packs/application.js
require('./hello_react.jsx')
```

webpackでbuildする。

```bash
$ bin/webpack
```

Railsサーバ起動後

```bash
$ rails s
```

起動が確認されたら `http://localhost:3000/pages/index` にアクセスする。 
下記のようなに `Hello React!` と表示されていればbuildされ無事に読み込まれています。

<img width="392" alt="スクリーンショット 2017-01-18 20.51.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/29570/e0f0debd-d0da-f15f-ebef-184b6659c778.png">


あとは、`app/javascript/packs/hello_react.js` にReactを記述できます。
（または `app/javascript/packs/application.js` に記述もできる。）


# より便利に使う（ビルドサーバ起動）

このままでは、変更を加えるたびに `bin/webpack` でbuildしないと反映されません。
そこで下記の手順で `webpack` のbuildサーバを起動させ、自動で変更が反映されるようにします。


まずは、buildされたコードにアクセスできるように、Railsの設定ファイルに変更を加えます。


webpackをインストールしたときに `Rails.application.configure` のブロック内に以下のようなコメントが追記されています。

```rb:config/environments/development.rb

Rails.application.configure do
  # Make javascript_pack_tag load assets from webpack-dev-server.
  # config.x.webpacker[:dev_server_host] = "http://localhost:8080"

  ...

end
```

` config.x.webpacker[:dev_server_host] = "http://localhost:8080" `のコメントを外して保存してください。







変更が終わったら、build用のサーバを起動します。
これでファイルを保存するたびにbuildが自動で行われます。

```bash
$ bin/webpack-dev-server
```

また、別のターミナルを起動し、Railsサーバも同時に立ち上げます。

```bash
$ rails s
```

これでファイルに変更があってもサイトに自動で反映されるようになります。（**Railsサーバは別に起動が必要です。**）

# 注意点

## `yarn` のインストールについて  
`yarn`（javascriptパッケージ管理）がインストールされてなくても`webpacker`の`bundle install`は正常に実行されます。（エラーが発生しません。）**必ずyarnをインストールしてから** `webpacker`のインストールを行ってください。

## `webpack, yarn`コマンドの実行について  
`webpack`コマンドを実行時には**必ず先頭に`bin/`をつけてください。**
また`yarn`のコマンドで新たにパッケージを追加するときも**必ず先頭に`bin/`をつけてください。**

`bin/`をつけないとwebpackerのwebpackコマンド（yarnコマンド）と認識されず、webpackerの意図した場所（ディラクトリ）にbuild（インストール）されません。

```bash:コマンド実行例
# webpackコマンド実行
$ bin/webpack
$ bin/webpack-dev-server

# yarnコマンド実行例
$ bin/yarn add bootstrap
```

# 参考サイト

http://qiita.com/hiyamamoto/items/e0a30b4799314174b80f
https://github.com/rails/webpacker


# 追記

2017/01/21 webpack-dev-server の設定方法の箇所に変更を加えました。
2017/01/22 注意点を追加しました。
2017/03/20 プルリクを反映（reaceインストール時に書き出されるファイル名の修正）
2017/03/20 WebpackerがAngular, Vueに対応したので追記