Rails5.1とWebpackerでモダンにRiot.jsを扱う

Webpackerを使ったRailsの新しいフロントエンド環境に慣れておくために、Riot.jsを導入し、ES6のコードをbabelでトランスコンパイルしてブラウザで動かすサンプルを作ってみます。
本番環境にデプロイすることも想定し、最終的にHerokuで動かすところまでやってみます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/97243/1eb1ec58-573c-3921-7501-96983f21dfde.png)

この記事では**Riot.js**を使います。お間違いのないように。。

## 1. rails new

まずはいつものようにRailsを初期化。``--webpack``オプションでWebpacker付きでインストールされる。

```bash
$ mkdir rails5.1-riot-example; cd $_
$ bundle init
```

Gemfileを編集して railsを有効化

```ruby:Gemfile
gem "rails"
```

```
$ bundle exec rails new . --webpack
```

gemのインストール後に``webpacker:install``タスクが自動的に実行され、``babel``や``webpack``などのNode.jsモジュールが``yarn``経由でインストールされる。
自動生成される``package.json``は以下のようになっていた。

```json:package.json
{
  "name": "rails5_1-riot-example",
  "private": true,
  "dependencies": {
    "autoprefixer": "^7.1.1",
    "babel-core": "^6.24.1",
    "babel-loader": "7.x",
    "babel-plugin-syntax-dynamic-import": "^6.18.0",
    "babel-plugin-transform-class-properties": "^6.24.1",
    "babel-polyfill": "^6.23.0",
    "babel-preset-env": "^1.5.1",
    "coffee-loader": "^0.7.3",
    "coffee-script": "^1.12.6",
    "compression-webpack-plugin": "^0.4.0",
    "css-loader": "^0.28.4",
    "extract-text-webpack-plugin": "^2.1.0",
    "file-loader": "^0.11.1",
    "glob": "^7.1.2",
    "js-yaml": "^3.8.4",
    "node-sass": "^4.5.3",
    "path-complete-extname": "^0.1.0",
    "postcss-loader": "^2.0.5",
    "postcss-smart-import": "^0.7.4",
    "precss": "^1.4.0",
    "rails-erb-loader": "^5.0.1",
    "resolve-url-loader": "^2.0.2",
    "sass-loader": "^6.0.5",
    "style-loader": "^0.18.1",
    "webpack": "^2.6.1",
    "webpack-manifest-plugin": "^1.1.0",
    "webpack-merge": "^4.1.0"
  },
  "devDependencies": {
    "webpack-dev-server": "^2.4.5"
  }
}
```


## 2. 空っぽのページを作る

```ruby:config/routes.rb
Rails.application.routes.draw do
  root 'application#index'
end 
```

```ruby:app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  def index
  end
end
```


```erb:app/views/application/index.html.erb

```

レイアウトからSprocketsのヘルパー (``stylesheet_link_tag``と``javascript_include_tag``) を消し、代わりに``javascript_pack_tag``を追加する。
``<%= javascript_pack_tag 'application' %>``とした場合、``public/packs/application.js``
``javascript_pack_tag``が示すJavaScriptファイルは後ほど作成する。

```erb:app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Rails51RiotExample</title>
    <%= csrf_meta_tags %>
  </head>

  <body>
    <%= yield %>
    <%= javascript_pack_tag 'application' %>
  </body>
</html>
```

## 3. Riotを使う為の環境設定
パッケージの名称に注意 (Riot本体はriot、loaderはriotjs-loader)。
``npm install``とは違い、``yarn add``はオプション無しで``package.json``が更新される。

```bash
$ bin/yarn add riot riotjs-loader
```

tagファイルをriotjs-loaderに投げる設定

```js:config/webpack/loaders/riot.js
module.exports = {
  test: /\.tag$/,
  loader: 'babel-loader!riotjs-loader'
}
```

グローバルでriotを参照できるようにする。

```js:config/webpack/shared.js
// ---
plugins: [
    new webpack.ProvidePlugin({riot: 'riot'}), // 追加
    new webpack.EnvironmentPlugin(JSON.parse(JSON.stringify(env))),
// ---
```

## 4. JavaScriptを用意

起点になるJavaScriptファイルを作成

```js:app/javascript/packs/application.js
const riot = require('riot')
require('../tags/app.tag')

document.addEventListener('DOMContentLoaded', () => {
  document.body.appendChild(document.createElement('app'))
  riot.mount('*')
})
```

Riot.jsのカスタムタグを作成
(1秒ごとに時刻を更新するだけのアプリ)

```html:app/javascript/tags/app.tag
<app>
  <div>
    <p>Hello world</p>
    <p>
      <strong>{time}</strong>
    </p>
  </div>

  <script>
    this.on('mount', () => {
      setInterval(this.updateTime, 1000)
    })

    updateTime() {
      this.update({time: Date()})
    }
  </script>
</app>
```

## 5. 動作確認

webpack-dev-serverを起動すると、変更を検知して自動でトランスパイルしてくれる。

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

別端末でrailsを起動し``http://localhost:3000/``で動作確認ができる。

## 6. Herokuで動かす

``Gemfile``にPostgreSQL用のgemを追加しておく。

```ruby:Gemfile
# ---
gem 'pg', group: :production
gem 'sqlite3', group: [:development, :test]
# ---
```

heroku上にアプリケーションを作成する。
WebpackでトランスコンパイルするためにNode.jsのビルドパックを追加する必要がある。Rubyのビルドパックも明示的に追加しておく。

```bash
$ heroku create
$ heroku addons:create heroku-postgresql:hobby-dev

# ビルドパックの追加
$ heroku buildpacks:add --index 1 heroku/nodejs
$ heroku buildpacks:add --index 2 heroku/ruby

# デプロイ
$ git push heroku master
$ heroku run rake db:migrate

# アプリケーションを開く
$ heroku open
```

production用としてちゃんとUglifyされている。
超かんたん！

## まとめ

フロントエンドと聞くと、npmやらbowerやらyoemanやらgruntやらgulpやら、環境構築が辛いイメージがありましたが、今回は設定ファイルをほぼ触らずに簡単にデプロイできたので驚きました。
片手間で~~テキトーに~~JS書いているような自分にはちょうどよい感じですね！(・∀・)