React 触ってみた
- 実は Qiita で記事書くの初めてです!どうぞお手柔らかに!
- 世の流れにとり残されてる感があったので、重い腰をあげて React に触れてみたのでその備忘録。
- できるだけモダンなフロントエンド開発をするための、素地を整えるところまでをゴールとする。
構成
- Rails 4.2.x
- React
- ES6
- Babel
- Webpack
- gulp
- Material Design Lite
方針
- 開発環境は 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 はちょっと癖がある。
表示!
さあ、ここまできたら、ブラウザで確認してみよう!
マテリアルデザインっぽいボタンがポツリと出現しているはず!
これにてモダンなフロントエンド開発の素地作り終了!
コード
https://github.com/mugiwaranonoma/react-test
一応 git にあげておいた。
今後
- gulpfile.js もっと書き込んでいかねば(gulp 触るのも初めてだった)
- redux 導入したい
- redux-router(?) で、SPA なのに URL がきちんと確認できるようにしたい
- ESLint いれたい
- Webpack と gulp が混在してるのどうにかしたい
- node_modules 以下のファイルを、Webpack もビルドできるのなら、Webpack だけにできそうな感じはある