Rails5+webpacker+Reactを試してみた

  • 44
    いいね
  • 0
    コメント

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 が使えない)

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

ダイジェスト値の付与を確認(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

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

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

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

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

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

$ bundle exec rails g controller Count index

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

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

スクリプト作成

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

.
├── 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

なんか動いてるっぽい。

所感

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

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

Rails 5.1に期待。