Help us understand the problem. What is going on with this article?

ExpressでReactを始める

More than 3 years have passed since last update.

はじめに

調べ方が悪かったのか、それとも調べなくてもわかるのが当たり前の世界なのかは不明だけど、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:thumbsup:

{
  "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

自動生成されたファイルの修正

自動生成されたファイルのソースコードを一部修正する。

app.js
@@ -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());

ルーティングの実装を変更するために下記のような内容に変更。

routes/index.js
@@ -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"と併せて別ファイルでルーティングを管理しつつコントローラーっぽいものを作ってみた。

routes/route_bind.js
module.exports = function (app, routes) {
  app.use('/', routes.demo);
};
controller/demo.js
var express = require('express');
var router  = express.Router();

router.get('/', function (req, res, next) {
  res.render('demo/index', {});
});

module.exports = router;

JSX

ViewをJSXで実装する。

views/demo/index.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

画面表示

実際に表示される画面はこんな感じ。

sampleView.png

最後に

"express-react-views" を利用することで、思っていたよりもだいぶ簡単に「Express+React」を実現することができた。
今度は"server-side rendering"とかSPAを試してみようかなと思います。

GitHub

今回やってみた内容をGitHubに公開してあるので参考にどうぞ。

https://github.com/asma-dev/ReactSample

asma
2016年8月からフリーランスとして活動しています。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした