はじめに
調べ方が悪かったのか、それとも調べなくてもわかるのが当たり前の世界なのかは不明だけど、Expressをベースにしてて、且つ自分が知りたい部分のReactの解説をしてるような情報が見つからなかったので残しておく。
やりたかったこと
シンプルに言えば、「ExpressのViewエンジンをReactにする」。ただそれだけ。
それだけのはずなんだけど、ピンポイント過ぎたのか思うような情報は得られなかった。
元々EJSを使っててReactに乗り換えてみようと思ったら、これが原因でいきなり行き詰まった。
んで、調べた結果なんとかなったので残しておこうかと。
ちなみに、今回は単純にReactで画面表示するとこまでできればそれでいいという内容です。
"server-side rendering"はしないし、ルーティングもExpressのRouterを利用するだけでSPAにはなってません。てゆーか画面複数作りません。
内容は薄いので、あんまり期待しないでください。
前提
あとで package.json の中身を載せるので、そこでも諸々確認できると思うけど根本的なとこを先に。
ちなみに、Node開発自体初心者じゃないことは大前提。
そこは考慮してないので、初心者だとわけわからないところもあると思います。
IDE
Cloud9を使う。
基本、Nodeの開発する時はいつもCloud9使ってるんで、Cloud9上の話だという前提で読まないとつまるとこがあるかも。
Nodeのバージョン
4.x系を使ってるので、0.10.x・0.11.x・0.12.xとかiojsとか、統合が決まる前のゴニョゴニョしてる時代のバージョンは考慮してない。
Node自体の情報には詳しくないので、後方互換なのかとかその辺の事情も知らないです。
そーゆーのが気になる人は、英語の記事読みあさってください。
npm
2.x系使ってます。
3.x系でも問題ないとは思ってるけど一応。
あと、インストールでエラーが出るような事象が発生した場合はこっちの記事を参考にしてみてください。解消するかもしれないです。
環境構築
プロジェクト作成
プロジェクト作成の前に
Express使ってる人は知ってるかもしれないけど、コマンドラインから簡単にプロジェクト生成ができるんで、先に express-generator をインストールしておく。
npm i -g express-generator
上記コマンドを実行して、インストールが完了したら以下のコマンドで確認。
express -h
ヘルプが表示されればOK。
Usage: express [options] [dir]
Options:
-h, --help output usage information
-V, --version output the version number
-e, --ejs add ejs engine support (defaults to jade)
--hbs add handlebars engine support
-H, --hogan add hogan.js engine support
-c, --css <engine> add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)
--git add .gitignore
-f, --force force on non-empty directory
プロジェクトを作成する
express-generator を利用して、コマンドラインからプロジェクト生成。
express ./ReactSample
一瞬でプロジェクトの生成が完了。
以下のようなメッセージが表示される。
create : ./ReactSample
create : ./ReactSample/package.json
create : ./ReactSample/app.js
create : ./ReactSample/public
create : ./ReactSample/public/javascripts
create : ./ReactSample/routes
create : ./ReactSample/routes/index.js
create : ./ReactSample/routes/users.js
create : ./ReactSample/public/stylesheets
create : ./ReactSample/public/stylesheets/style.css
create : ./ReactSample/views
create : ./ReactSample/views/index.jade
create : ./ReactSample/views/layout.jade
create : ./ReactSample/views/error.jade
create : ./ReactSample/public/images
create : ./ReactSample/bin
create : ./ReactSample/bin/www
install dependencies:
$ cd ./ReactSample && npm install
run the app:
$ DEBUG=ReactSample:* npm start
どんなディレクトリやファイルが生成されたのかと、このあとはどうすればいいのかのヒントっぽいものが表示される感じ。
これでとりあえずプロジェクトの作成が完了。
npmで色々インストール
グローバルなやつ
-
forever
Nodeをデーモン化するおなじみのやつ。
ほかにも色々選択肢はあるんだと思うけど、いつもこれ使ってます。 -
webpack
browserify と同じ理由でインストール。
以下、インストールコマンド。
npm i -g forever webpack
プロジェクト固有なやつ
プロジェクトルートに置かれてる package.json に、必要なモジュールを追記して一気にインストール。
以下の内容に書き換える。まるっとコピペでもOK
{
"name": "ReactSample",
"version": "0.0.0",
"private": true,
"engine": {
"node": "4.x",
"npm": "2.x"
},
"dependencies": {
"async": "^1.5.0",
"babel-preset-react": "^6.1.18",
"babelify": "^7.2.0",
"body-parser": "^1.14.1",
"cookie-parser": "^1.4.0",
"debug": "^2.2.0",
"express": "^4.13.3",
"express-react-views": "^0.9.0",
"react": "^0.14.3",
"react-bootstrap": "^0.27.3",
"react-dom": "^0.14.3",
"serve-favicon": "^2.3.0"
}
}
プロジェクト作成時に自動生成される package.json にはdependenciesが記述されている。それを流用してるからそのまま残してる記述もある。
基本的には、Reactの公式サイトにある「Get Started」の中に掲載されているものをそのまま追記しているだけ。
今回重要なのはReactの公式サイトには載ってないもので、 "express-react-views" ってやつ。
名前そのままなんだけど、こいつを使えばReactをExpressのViewエンジンとして利用できるようになる。
package.json の修正が終わったら、プロジェクトルートで npm i
する。
Hello world
自動生成されたファイルの修正
自動生成されたファイルのソースコードを一部修正する。
@@ -1,29 +1,26 @@
-var express = require('express');
-var path = require('path');
-var favicon = require('serve-favicon');
-var logger = require('morgan');
+var express = require('express');
+var path = require('path');
+var favicon = require('serve-favicon');
var cookieParser = require('cookie-parser');
-var bodyParser = require('body-parser');
-
-var routes = require('./routes/index');
-var users = require('./routes/users');
+var bodyParser = require('body-parser');
+var routes = require('./routes');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
-app.set('view engine', 'jade');
+app.set('view engine', 'jsx');
+app.engine('jsx', require('express-react-views').createEngine());
// uncomment after placing your favicon in /public
-//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
-app.use(logger('dev'));
+app.use(favicon(path.join(__dirname, 'public/img', 'asma_icon_32x32.ico')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
-app.use('/', routes);
-app.use('/users', users);
+// routing
+require('./routes/route_bind')(app, routes);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
下記の箇所でViewエンジンとしてReactを使用するように設定している。
あとはfaviconを指定したりルーティングを変更したりしてる感じ。
app.set('view engine', 'jsx');
app.engine('jsx', require('express-react-views').createEngine());
ルーティングの実装を変更するために下記のような内容に変更。
@@ -1,9 +1,13 @@
-var express = require('express');
-var router = express.Router();
+/**
+ * routes list
+ *
+ * @package Route
+ * @author asma
+ * @copyright 2015 asma All Rights Reserved.
+ */
-/* GET home page. */
-router.get('/', function(req, res, next) {
- res.render('index', { title: 'Express' });
-});
+var routes = {
+ demo: require('../controller/demo')
+};
-module.exports = router;
+module.exports = routes;
自動生成されるViewファイルはJadeで記述されていたのでJSXに変更する。
"views/error.jade"を"views/error.jsx"に変更して以下のような内容に修正。
var React = require('react');
var errPage = React.createClass({
render: function () {
return (
<div className="contents">
<h1>{this.props.message}</h1>
<h2>{this.props.error.status}</h2>
<pre>{this.props.error.stack}</pre>
</div>
);
}
});
module.exports = errPage;
新規に作ったファイル
ルーティング関連
自動生成された記述のままだと、ルーティングが増える度に"app.js"に行を追加しなければならないような感じになっているので、"routes/index.js"と併せて別ファイルでルーティングを管理しつつコントローラーっぽいものを作ってみた。
module.exports = function (app, routes) {
app.use('/', routes.demo);
};
var express = require('express');
var router = express.Router();
router.get('/', function (req, res, next) {
res.render('demo/index', {});
});
module.exports = router;
JSX
ViewをJSXで実装する。
var React = require('react');
var ReactBS = require('react-bootstrap');
var Content = React.createClass({
getDefaultProps: function () {
return {
title: 'ReactSample',
outputWord: 'Hello World!!'
};
},
render: function () {
return (
<html lang="ja">
<head>
<meta charSet="UTF-8" />
<title>{this.props.title}</title>
<meta name="author" content="asma" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
{/* styles */}
<link type="text/css" rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css" />
<link type="text/css" rel="stylesheet" href="/css/demo.css" />
{/* /styles */}
{/* scripts */}
<script charSet="UTF-8" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script charSet="UTF-8" src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
{/* /scripts */}
</head>
<body>
{/* header */}
<ReactBS.ButtonGroup justified>
<ReactBS.Button href="#">ボタン01</ReactBS.Button>
<ReactBS.Button href="#">ボタン02</ReactBS.Button>
<ReactBS.DropdownButton title="ドロップダウン" id="bg-justified-dropdown" data-toggle="dropdown">
<ReactBS.MenuItem eventKey="1">リンク01</ReactBS.MenuItem>
<ReactBS.MenuItem eventKey="2">リンク02</ReactBS.MenuItem>
</ReactBS.DropdownButton>
</ReactBS.ButtonGroup>
{/* /header */}
{/* container */}
<div className="container-fluid">
<h1 className="helloWorld">{this.props.outputWord}</h1>
</div>
{/* /container */}
</body>
</html>
);
}
});
module.exports = Content;
その他
ディレクトリ名の変更
自動生成されたディレクトリ名のままだとなんかちょっとな。。。というのを見慣れた感じに変更した。
public/images → img
public/javascripts → js
public/stylesheets → css
削除したファイル
不要なので削除したファイルは以下。
routes/users.js
views/layout.jade
画面表示
実際に表示される画面はこんな感じ。
最後に
"express-react-views" を利用することで、思っていたよりもだいぶ簡単に「Express+React」を実現することができた。
今度は"server-side rendering"とかSPAを試してみようかなと思います。
GitHub
今回やってみた内容をGitHubに公開してあるので参考にどうぞ。