Rails & Webpackerでフロントエンド開発環境を整える

# 概要
Rails 5.1 から導入されるという[webpacker](https://github.com/rails/webpacker)を試してみます。
## Webpackerとは
> Webpacker makes it easy to use the JavaScript preprocessor and bundler Webpack to manage application-like JavaScript in Rails. It coexists with the asset pipeline, as the purpose is only to use Webpack for app-like JavaScript, not images, css, or even JavaScript Sprinkles (that all continues to live in app/assets).

> It's designed to work with Rails 5.1+ and makes use of the Yarn dependency management that's been made default from that version forward. It's also currently compatible with Rails 5.0 stable but there's absolutely no warranty it will still be in the future. You can either make use of Webpacker during setup of a new application with --webpack or you can add the gem and run bin/rails webpacker:install in an existing application.

要は従来のasset piplineとwebpackをつかったJSアプリケーションを共存させるための仕組みといったところでしょうか。

# 環境
macOS Sierra 10.12.2
Ruby 2.3.0
Rails 5.0.1

# 導入
まずはrails new

```zsh
$ rails new webpacker-sample
```

## webpackerをインストール
Gemfileに追記して`bundle install`[^1]

```diff
diff --git a/Gemfile b/Gemfile
index a8f01f7..1e86672 100644
--- a/Gemfile
+++ b/Gemfile
@@ -19,6 +19,8 @@ gem 'jbuilder', '~> 2.5'
 # Use ActiveModel has_secure_password
 # gem 'bcrypt', '~> 3.1.7'
 
+gem 'webpacker', github: "rails/webpacker"
+
 # Use Capistrano for deployment
 # gem 'capistrano-rails', group: :development
```

```zsh
$ bundle install
```

するとRakeタスクにwebpacker用のタスクが追加されます。

```zsh
$ bin/rake -T | grep webpacker
rake webpacker:compile                  # compile javascript packs using webpack for production with digests
rake webpacker:install                  # install webpacker in this application
rake webpacker:install:react            # install everything needed for react
```

`webpacker:install` を叩いてみる

```zsh
$ bin/rake webpacker:install
```

必要なファイルやコマンド郡が追加されました。

```
new file:   app/javascript/packs/application.js
new file:   bin/webpack
new file:   bin/webpack-watcher
new file:   bin/yarn
new file:   config/webpack/development.js
new file:   config/webpack/production.js
new file:   config/webpack/shared.js
new file:   vendor/package.json
new file:   vendor/yarn.lock
```

これでとりあえずwebpackを使う準備が整いました。

package.jsonの中身はこんな感じ。 シンプルに`devDependencies`のみとなっています。

```json
{
  "devDependencies": {
    "babel-core": "^6.21.0",
    "babel-loader": "^6.2.10",
    "babel-preset-latest": "^6.16.0",
    "coffee-loader": "^0.7.2",
    "coffee-script": "^1.12.2",
    "path-complete-extname": "^0.1.0",
    "rails-erb-loader": "^3.2.0",
    "webpack": "beta",
    "webpack-merge": "^2.3.1"
  }
}
```

## ビルドしてみる
ビルドは`bin/`配下に追加された`bin/webpack`,`bin/webpack-watcher`,`webpack:compile`を使用します。

```zsh
$ bin/webpack
Hash: 071bb7221801f498ad46
Version: webpack 2.2.0-rc.3
Time: 400ms
             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]
```

`app/javascript/packs/application.js`がビルドされて `public/packs/` に `application.js`と`application.js.map`というファイルが生成されました。

ビルド時には`config/webpack/*.js`がconfファイルとして使用されます。
また、エントリーポイントは`app/javascript/packs/`配下に配置し、それ以外のファイルは`app/javascript/`に配置する設計となっているそうです。

例えばカレンダーアプリを作成する場合

```js:app/javascript/calendar/index.js
console.log('Hello, Calendar!')
```

```js:app/javascript/packs/calendar.js
require('calendar')
```
のようにファイルを配置してビルドします。

```zsh
$ bin/webpack
Hash: cafcaf55f1653cfef821
Version: webpack 2.2.0-rc.3
Time: 421ms
             Asset     Size  Chunks             Chunk Names
       calendar.js  3.22 kB       0  [emitted]  calendar
    application.js  3.38 kB       1  [emitted]  application
   calendar.js.map     3 kB       0  [emitted]  calendar
application.js.map  3.35 kB       1  [emitted]  application
   [0] ../app/javascript/calendar/index.js 32 bytes {0} [built]
   [1] ../app/javascript/packs/application.js 496 bytes {1} [built]
   [2] ../app/javascript/packs/calendar.js 20 bytes {0} [built]
```

プロダクション用にビルドする場合は`rake webpacker:compile`を使用すると、`public/packs/digest.json`とハッシュ値つきのjsファイルが生成されます。

また、webpackのウォッチ機能を使いたい場合は`bin/webpack-watcher`を使用します。

## インクルード
ビルドしたjsをインクルードするには `javascript_include_tag`の代わりに`javascript_pack_tag`というヘルパーメソッドを利用します。

```rb:app/views/layouts/application.html.erb
<%= javascript_pack_tag 'calendar' %>
```

このメソッドを使うことでproduction環境ではdigest化されたjsがインクルードされるようになります。
production環境以外でdigest化されたjsをインクルードするには`config/environments/*.rb`に下記のように追記します。

```rb
config.x.webpacker[:digesting] = true
```
## webpack-dev-server
[こちらのPR](https://github.com/rails/webpacker/pull/55)で追加されました。
`bin/webpack-dev-server`で起動します。
以下のように有効化すると`javascript_pack_tag`で`localhost:8080`(デフォルトポート)でサーブされているjsファイルをインクルードしてくれます。

```rb:development.rb
  # Make javascript_pack_tag load assets from webpack-dev-server.
  config.x.webpacker[:dev_server_host] = "http://localhost:8080"
```

# その他
Reactを利用する場合は`webpacker:install:react`というrakeタスクでいい感じにファイルを生成してくれます。
ですがこのあたりはまだまだ[変わっていきそう？](https://github.com/rails/webpacker/pull/25)ですね。 
[react導入サンプル](https://github.com/hiyamamo/webpacker-sample)

# 所感
まだまだwebpacker自体は開発が始まったばかりですが、すでにこれだけでもフロント開発していけそうな気がします。
ServerSideRenderingする場合は[react_on_rails](https://github.com/shakacode/react_on_rails)等と組み合わせて使ってもよさそうです。


# 参考
http://qiita.com/itkrt2y/items/69c31e695b1b39915d00
[^1]: Rail 5.1では`rails new --webpack`とすると自動で追加されるようです