LoginSignup
18
13

More than 5 years have passed since last update.

React-Redux on Rails(w/webpack/yarn)環境を作ったメモ

Last updated at Posted at 2017-05-30

やりたいこと

はいからなフロントエンド開発にあこがれてReact(とReduxその他)を最近はじめました。

とりあえずBabel/browser.jsを使って画面をロードするたびに毎回トランスパイルするような環境で勉強していたのですが、実務レベルで利用していくにあたって以下のような環境を作ることが課題です。

  1. BabelによってReactコード(JSX/ES6)を静的にトランスパイルし、ファイル出力する
  2. ファイル(モジュール)単位に分散させたコードを依存解決し、バンドルしてファイル出力する
  3. 1-2を単体テストや難読化等と組み合わせてタスク化し運用する
  4. 1-3をサーバサイドのタスクやデプロイ手順に組み込み、同時に運用する

Rails上にこのような環境を構築するためのツールとしては以下がみつかりまして、

ツール 機能
gem sprockets-commoner アセット作成と同時にAltJSをトランスパイルして、アセットに組み込めるようにする
gem webpacker Rails公式。webpack/yarnベースでフロント環境構築を補助。Rails5.1で正式対応。Reactなど復数フレームワーク対応
gem react-rails React公式。webpack/npmベースでフロント開発構築を補助。ReactオブジェクトをRailsのView側から操作できる機能なども

試しにwebpackerをちょっと使ってみたところ非常に簡単に環境構築できました。ただ裏側で色々やってくれすぎてしまって、設定ファイルなどなかなか複雑な様子。

ツール類の理解が不十分な状態でこれを運用するのには不安が大きく、やはり自力で最小構成を作って順次改善していこうという流れになりました。

以下、上に挙げた課題1と2を満たす環境の構築手順についての覚え書きです。

概要

利用するパッケージ

今回は主に以下のパッケージを利用して、フロント環境をRails上に構築する。

npmパッケージ 用途
yarn フロントのパッケージ管理(npmの代替)
webpack フロントのバンドル(ファイル結合・依存関係解決)
babel Reactコード(ES6, JSX)のトランスパイル
react コンポーネントの構築・管理
redux 状態モデルの構築・管理
immutable Redux reducerの実装時に不変オブジェクトを利用
bootstrap UIデザイン用
jquery UIデザイン用

ファイル構成

フロント環境に関連するファイルは以下のように配置する。

app/
    assets/
        javascript/     // アセットパイプライン管理のスクリプトを格納(今回は関係なし)
    scripts/            // webpackのバンドル対象のjsファイルを格納
        actions/            // Reduxのアクション
        components/         // Reactプレゼンテーションコンポーネント
        containers/         // Reactコンテナコンポーネント
        reducers/           // Reduxのリデューサ
        entries/            // webpackのエントリポイント
public/
    scripts/            // webpackがバンドルしたjsファイルを(自動的に)格納

node_modules/           // npmパッケージを格納
package.json            // yarnによるパッケージ情報
yarn.lock               // yarnによるバージョン設定
webpack.config.js       // webpackの設定を記述

構築

プロジェクトの作成

作成済みの場合は省略

$ rails new rrrdemo
$ cd rrrdemo
$ bundle install
.gitignore
# バンドルファイルを管理から除外
public/scripts/*

npmパッケージのインストール

必要なパッケージをインストールする。

# 管理状態を初期化
$ yarn

# パッケージをインストール
$ yarn add react react-dom react-redux redux immutable jquery bootstrap
$ yarn add --dev webpack webpack-dev-server babel-loader babel-core babel-preset-es2015 babel-preset-react

webpackの設定

webpackの設定ファイルを作る。

webpack.config.js
var path = require('path');

module.exports = {
    // エントリの定義
    entry : {
        // TODO: SPA(画面)が増えるたびに逐次追加する
    },

    // 出力先の定義: /public/scripts/<エントリ名>.bundle.js に出力される
    output : {
        path        : path.resolve(__dirname, 'public/scripts/'),
        filename    : '[name].bundle.js',
    },

    // ローダーの設定: .jsファイルはバンドル前にbabelでトランスパイルする
    module: {
        rules: [
            {
                test: /\.js$/,
                use: [{
                    loader: 'babel-loader',
                    options: {
                        presets: [ 'es2015', 'react' ],
                    },
                }],
            },
        ],
    },

    // webpack-dev-serverの設定
    devServer : {
        port            : 8080,
        inline          : true,
        clientLogLevel  : 'info',
        contentBase     : path.join(__dirname, 'scripts/'),
        watchOptions    : {
            poll            : true
        },
    },

    // Source Mapの設定
    devtool : "cheap-eval-source-map",
};

node_modulesの設定

node_modules/をバージョン管理に含める必要はないのでignoreしておく。(Rails5以降デフォルト)

.gitignore
/node_modules
/yarn-error.log

node_modules/以下のファイルをアセットパイプラインからも参照できるようにしておく。アセットを使わない前提の場合は不要(今回はbootstrap.cssをアセット経由で使いたい)
(これもRails5以降デフォルト)

config/initializers/assets.rb
Rails.application.config.assets.paths << Rails.root.join('node_modules')

確認

デモ用の画面を2つ作り、それぞれが依存解決されたバンドルファイルを実行できることを確認する。

デモ画面の作成

各画面は、webpackが作成するバンドル済みのファイルを読めるようにしておく。

$ rails g controller demo
config/route.rb
+ get '/demo/a' => 'demo#a'
+ get '/demo/b' => 'demo#b'
app/controllers/demo_controller.rb
+ def a
+ render 'a'
+ end
+ 
+ def b
+ render 'b'
+ end
app/assets/stylesheets/application.css
+ @import 'bootstrap/dist/css/bootstrap';
app/views/demo/demo_a.html.erb
<h3>Demo A</h3>
<div class="container">
  <div class="row">
    <div class="col-md-12">
      <div id="sandbox"></div>
    </div>
  </div>
</div>
<script src="/scripts/demo_a.bundle.js" type="text/javascript"></script>
app/views/demo/demo_b.html.erb
<h3>Demo B</h3>
<div class="container">
  <div class="row">
    <div class="col-md-12">
      <div id="sandbox"></div>
    </div>
  </div>
</div>
<script src="/scripts/demo_b.bundle.js" type="text/javascript"></script>

エントリポイントの登録

各画面のエントリファイルを(まずはファイルだけ)作成する。

app/scripts/entries/demo_a.js
console.log('demo A loaded');
app/scripts/entries/demo_b.js
console.log('demo B loaded');

エントリファイルをwebpackの設定に追加する。

webpack.config.js
    entry : {
+        demo_a  : path.resolve(__dirname, 'app/scripts/entries/demo_a.js'),
+        demo_b  : path.resolve(__dirname, 'app/scripts/entries/demo_b.js'),
    },

開発環境の起動

デモ用のサーバを起動する。
今回はwebrick + webpack(watchモード)を使う。
(webpack-dev-serverも使えるが、erbを解釈しないので後々面倒になりそう)

# それぞれ別窓やバックグラウンド等で同時に実行する
$ rails s
$ webpack --watch --progress --config webpack.config.js

以下のURLを開き、画面表示とコンソールのログ表示を確認する。

  • http://localhost:3000/demo/a
  • http://localhost:3000/demo/b

以降はjsファイルを保存して画面リロードするだけ最新状態が確認可能になる。

デモの実装

jQueryとReactが使えることを確認してみる。

デモ用のスクリプトを作る。

app/scripts/components/Message.js
import React from 'react';
import ReactDOM from 'react-dom';

export default class Message extends React.Component{
    render(){
        return (<div className="alert alert-success" role="alert">
            {this.props.label}
        </div>);
    }
};
app/scripts/entries/demo_a.js
import $ from 'jquery';
import React from 'react';
import ReactDOM from 'react-dom';
import Message from '../components/Message';

$(function(){
    ReactDOM.render(
        <Message label="Demo A" />,
        $('#sandbox')[0]
    );
});
app/scripts/entriesdemo_b.js
import $ from 'jquery';
import React from 'react';
import ReactDOM from 'react-dom';
import Message from '../components/Message';

$(function(){
    ReactDOM.render(
        <Message label="Demo B" />,
        $('#sandbox')[0]
    );
});

デモ用の画面を確認する。

  • http://localhost:3000/demo/a
  • http://localhost:3000/demo/b

各画面にReactコンポーネント(緑色のアラート表示)が表示されることが確認できたらOK。

demoA.png

demoB.png

18
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
13