React 触ってみたよ!

  • 15
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

React 触ってみた

  • 実は Qiita で記事書くの初めてです!どうぞお手柔らかに!
  • 世の流れにとり残されてる感があったので、重い腰をあげて React に触れてみたのでその備忘録。
  • できるだけモダンなフロントエンド開発をするための、素地を整えるところまでをゴールとする。

構成

方針

  • 開発環境は vagrant(ubuntu) でやる
  • 既存の Rails アプリケーションに乗せる
  • フロントエンドの管理に関するものは、すべて npm で管理する(gem ではやらない)
  • フロントエンドに関するコードはすべて app_root/frontend に置く
  • 自分で書いた js, jsx 部
    • app_root/app/assets/javascripts/dist/main.js に出力する
  • 外部ライブラリ js 部
    • app_root/app/assets/javascripts/dist/vendor.js に出力する
  • 自分で書いた css 部
    • app_root/app/assets/stylesheet/dist/main.css に出力する
  • 外部ライブラリ css 部
    • app_root/app/assets/stylesheet/dist/vendor.css に出力する

まず Rails から

軽くセットアップ

command
$ cd ~/
$ rails new react-test -d mysql
$ cd react-test
$ bin/rake db:create db:migrate

検証に必要な分だけの、最低限のコード書く

Gemfile.rb
gem 'slim-rails'
command
$ bin/rails g controller test
app/controllers/test_controller.rb
class TestController < ApplicationController
  def index
  end
end
app/views/test/index.html.slim
#container
config/routes.rb
root 'test#index'
command
$ bin/rails s -b 0.0.0.0

これで準備完了。

React 導入準備

node いれる

command
$ curl -L git.io/nodebrew | perl - setup
# .bashrc, .zshrc などに PATH 書く
export PATH=$HOME/.nodebrew/current/bin:$PATH
$ source ~/.zshrc
$ nodebrew ls-remote
# 20160603 時点での Stable は v5.11.1 だった(https://nodejs.org/en/blog/)
$ nodebrew install-binary v5.11.1
$ nodebrew use v5.11.1
$ node -v
v5.11.1
$ npm -v
3.8.6

app_root/frontend 以下の準備

command
$ cd ~/react-test
$ mkdir frontend
$ cd frontend
$ npm init
# とりあえず全部エンター押しとく

これで package.json がとりまできた。

パッケージ準備

React + ES6 + Babel + Webpack + gulp + Material Design Lite を使えるようにする

command
# React 用
$ npm install --save react react-dom react-router react-tap-event-plugin
# ES6, Babel, Webpack 用
$ npm install --save-dev babel babel-loader babel-core babel-preset-react babel-preset-es2015 webpack
# gulp 用
$ npm install --save-dev gulp gulp-autoprefixer gulp-concat gulp-concat-css gulp-ruby-sass gulp-webpack
# Material Design Lite 用
$ gulp install --save material-design-lite

--save は「パッケージ(≒プロジェクト)の実行に必要なパッケージの定義」らしい
--save-dev は「パッケージの開発に必要なパッケージの定義」らしい

package.json
{
  "name": "frontend",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel": "^6.5.2",
    "babel-core": "^6.9.1",
    "babel-loader": "^6.2.4",
    "babel-preset-es2015": "^6.9.0",
    "babel-preset-react": "^6.5.0",
    "gulp": "^3.9.1",
    "gulp-autoprefixer": "^3.1.0",
    "gulp-concat": "^2.6.0",
    "gulp-concat-css": "^2.3.0",
    "gulp-ruby-sass": "^2.0.6",
    "gulp-webpack": "^1.5.0",
    "webpack": "^1.13.1"
  },
  "dependencies": {
    "material-design-lite": "^1.1.3",
    "react": "^15.1.0",
    "react-dom": "^15.1.0",
    "react-router": "^2.4.1",
    "react-tap-event-plugin": "^1.0.0"
  }
}

ここまでで、package.json はこうなった。

ビルド周り

package.json
"scripts": {
  "build": "gulp build"
},

package.json を上記のように編集する。
これで、$ npm run build で、 $ gulp build が走るようになる。

command
$ cd ~/react-test/frontend
$ vim gulpfile.js
gulpfile.js
var gulp = require('gulp');
var webpack = require('gulp-webpack');
var concat = require('gulp-concat');
var sass = require('gulp-ruby-sass');
var autoprefixer = require('gulp-autoprefixer');
var concatCss = require('gulp-concat-css');

const JS_SRC = '';
const JS_DEST = '../app/assets/javascripts/dist';
const CSS_SRC = 'src/stylesheets/main.scss';
const CSS_DEST = '../app/assets/stylesheets/dist';

var webpackConfig = {
  entry: {
    main: './src/javascripts/main.js',
  },
  output: {
    filename: 'main.js'
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel?presets[]=react,presets[]=es2015'
      }
    ]
  }
}

gulp.task('js:main', function() {
  gulp.src(JS_SRC)
  .pipe(webpack(webpackConfig))
  .pipe(gulp.dest(JS_DEST));
});

gulp.task('js:vendor', function() {
  gulp.src([
    'node_modules/material-design-lite/material.min.js'
  ])
  .pipe(concat('vendor.js'))
  .pipe(gulp.dest(JS_DEST));
});

gulp.task('css:main', function(){
  sass(CSS_SRC, { style: 'expanded', bundleExec: true })
  .pipe(autoprefixer())
  .pipe(concatCss('main.css'))
  .pipe(gulp.dest(CSS_DEST));
});

gulp.task('css:vendor', function() {
  gulp.src([
    'node_modules/material-design-lite/material.min.css'
  ])
  .pipe(concatCss('vendor.css'))
  .pipe(gulp.dest(CSS_DEST));
});

gulp.task('build', ['js:main', 'js:vendor', 'css:main', 'css:vendor']);

これで、

  • 自分で書いた js, jsx 部
    • app_root/app/assets/javascripts/dist/main.js に出力する
  • 外部ライブラリ js 部
    • app_root/app/assets/javascripts/dist/vendor.js に出力する
  • 自分で書いた css 部
    • app_root/app/assets/stylesheet/dist/main.css に出力する
  • 外部ライブラリ css 部
    • app_root/app/assets/stylesheet/dist/vendor.css に出力する

という目的が達成できるようになった。

ディレクトリ構成

これで開発準備は整った。
下記実現したいディレクトリ構成。

app/frontend
└── app/frontend
    ├── gulpfile.js
    ├── package.json
    ├── src
    │   ├── javascripts
    │   │   ├── components
    │   │   │   └── test_page
    │   │   │       ├── fuga.js
    │   │   │       ├── hoge.js
    │   │   │       └── index.js
    │   │   ├── main.js
    │   │   └── utils
    │   └── stylesheets
    │       ├── main.scss
    │       └── pages
    │           └── test_page.scss
    └── test

ここでもう一度 Rails 側の設定をする

config/initializers/assets.rb
Rails.application.config.assets.precompile += %w(dist/main.js)
Rails.application.config.assets.precompile += %w(dist/vendor.js)
Rails.application.config.assets.precompile += %w(dist/main.css)
Rails.application.config.assets.precompile += %w(dist/vendor.css)

これで、 */dist 以下に出力されたファイルを、アセットパイプラインに乗せて、Rails 側から指定できるようになる。

app/views/layouts/application.html.slim
= javascript_include_tag 'dist/main'
= javascript_include_tag 'dist/vendor'
= stylesheet_link_tag 'dist/main'
= stylesheet_link_tag 'dist/vendor'
= stylesheet_link_tag 'https://fonts.googleapis.com/iconfamily=Material+Icons'

<body>...</body> の最後で読み込む必要がある点に注意。

main.js について

app/frontend/src/javascripts/main.js
import React from 'react';
import ReactDom from 'react-dom';
import { Router, Route, Link, browserHistory } from 'react-router';
import injectTapEventPlugin from "react-tap-event-plugin";

injectTapEventPlugin();

import TestPage from './components/test_page/index';

ReactDom.render((
  <Router history={browserHistory}>
    <Route path='/' component={TestPage} />
  </Router>
),document.querySelector('#container'));
  • main.js は Webpack と gulp によって、app_root/app/assets/javascripts/dist/main.js にビルドされる。
  • react-router により、マッチした path があると、component={XXXXXX} で指定したコンポーネントを表示してくれる。
  • Rails 側で用意した #container に、React で書いた分をぼこっとマウントする感じ。

React で表示してみる

frontend/src/javascripts/components/test_page/index.js
import React from 'react';

export default class TestPage extends React.Component {
  constructor() {
    super();
    this.state = {
    };
  }

  render() {
    return (
      <div className='mdl-grid'>
        <div className='mdl-cell mdl-cell--2-offset mdl-cell--8-col'>
          <button className='mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect'>ALL IS WELL!</button>
        </div>
      </div>
    );
  }
}
  • js の予約語との兼ね合いにより、class => className, for => htmlfor と書かなければならないなど、jsx はちょっと癖がある。

表示!

さあ、ここまできたら、ブラウザで確認してみよう!
マテリアルデザインっぽいボタンがポツリと出現しているはず!
これにてモダンなフロントエンド開発の素地作り終了!
スクリーンショット 2016-06-04 0.58.01.png

コード

https://github.com/mugiwaranonoma/react-test
一応 git にあげておいた。

今後

  • gulpfile.js もっと書き込んでいかねば(gulp 触るのも初めてだった)
  • redux 導入したい
  • redux-router(?) で、SPA なのに URL がきちんと確認できるようにしたい
  • ESLint いれたい
  • Webpack と gulp が混在してるのどうにかしたい
  • node_modules 以下のファイルを、Webpack もビルドできるのなら、Webpack だけにできそうな感じはある