React 触ってみたよ!

More than 3 years have passed since last update.


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 だけにできそうな感じはある