node.js+Express+mongooseの掲示板をReact view(SSR)で実装
前回の「node.js+Express+mongooseで掲示板を作ってみる」ではview engineをpugで実装しました。 これを機能はそのままに Reactをview engineとして使ってみます。
目次
- 目次
- 1. express-react-views パッケージインストール
- 2. app.js で view engine を変更する
- 3. ViewテンプレートをReact,JSXで記述
- 4. Reactテンプレートへのスタイルの適用
- 5. まとめ
1. express-react-views パッケージインストール
express-react-views は React, React-DOM を用いたテンプレートをサーバーサイドでコンパイル(トランスパイル)してHTMLを出力するパッケージです。
他のviewエンジンと役割は同じです。
プロジェクトディレクトリ内kakiapp/
でインストールします
$ cd kakiapp
$ npm install express-react-views react react-dom
+ express-react-views@0.10.4
+ react@16.0.0
+ react-dom@16.0.0
前回使ったpubは削除します
$ npm uninstall pug
2. app.js で view engine を変更する
app.js
var express = require('express'); var path = require('path'); var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var index = require('./routes/index'); var users = require('./routes/users'); var up = require('./routes/up'); var app = express(); //// 1. mongoose connection var mongoose = require('mongoose'); // mongoose 利用 mongoose.Promise = global.Promise; mongoose.connect('mongodb://localhost/app1', {useMongoClient: true}); // 接続 //// end 1 //// 2. mongoose model 定義 // Schema 定義 var postSchema = mongoose.Schema({ kakikomi: String, name: String }); // model var Post = mongoose.model('Post',postSchema); // Post modelを router moduleで共有する app.set('Post',Post); //// end 2 // view engine setup app.set('views', path.join(__dirname, 'views')); 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(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', index); app.use('/users', users); //// 3. 投稿画面 app.use('/up', up); //// end 3 // catch 404 and forward to error handler app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); // error handler app.use(function(err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.render('error'); }); module.exports = app;
今回の変更はviewにpugを使用していた以下のところ
// view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'pug');
を
// view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'jsx'); app.engine('jsx', require('express-react-views').createEngine());
と変更しました。views/ ディレクトリの テンプレートを全てをjsxで書き直します
3. ViewテンプレートをReact,JSXで記述
スタイルなしバージョン
ページレイアウト(共通)
layout.jsx
var React = require('react'); class Layout extends React.Component { render() { return ( <html> <head><title>{this.props.title} - 掲示板</title></head> <body> <header> <h1>掲示板</h1> <nav> <ul> <li><a href="/">トップ</a></li> <li><a href="/up">投稿</a></li> </ul> </nav> </header> {this.props.children} </body> </html> ); } } module.exports = Layout;
トップページ
index.jsx
var React = require('react'); var Layout = require('./layout'); class Index extends React.Component { render() { var list = []; for (var i in this.props.posts) { list.push( <li> <div className="box"> <div>{this.props.posts[i].kakikomi}</div> <p className="name">{this.props.posts[i].name}</p> </div> </li> ); } return ( <Layout title={this.props.title}> <div> <h2>{this.props.title}</h2> <ul> {list} </ul> </div> </Layout> ); } } module.exports = Index;
投稿ページ
up.jsx
var React = require('react'); var Layout = require('./layout'); class Up extends React.Component { render() { return ( <Layout> <h2>投稿フォーム</h2> <form action="/up" method="POST"> <label>内容</label> <textarea name="kakikomi"></textarea> <label>名前</label> <input type="text" name="name" /> <button type="submit">投稿</button> </form> </Layout> ); } } module.exports = Up;
投稿後ページ
up_post.jsx
var React = require('react'); var Layout = require('./layout'); class UpPost extends React.Component { render() { return ( <Layout> <h2>投稿しました</h2> <p>{this.props.kakikomi}</p> <p>{this.props.name}</p> </Layout> ); } } module.exports = UpPost;
4. Reactテンプレートへのスタイルの適用
layout.jsx
var React = require('react'); var style = { header: {marginBottom:"40px"}, navul: {display:"flex",listStyle:"none" }, navli: {marginRight:"20px"} } class Layout extends React.Component { render() { return ( <html> <head> <title>{this.props.title} - 掲示板</title> <link rel="stylesheet" href="/stylesheets/style.css" /> </head> <body> <header style={style.header}> <h1>掲示板</h1> <nav> <ul style={style.navul}> <li style={style.navli}><a href="/">トップ</a></li> <li style={style.navli}><a href="/up">投稿</a></li> </ul> </nav> </header> {this.props.children} </body> </html> ); } } module.exports = Layout;
index.jsx
var React = require('react'); var Layout = require('./layout'); var style = { ul: {listStyle:"none"}, name: {fontSize:"0.8em",color:"#555"}, box: {border:"1px solid #888", padding:"20px", marginBottom:"40px"} } class Index extends React.Component { render() { var list = []; for (var i in this.props.posts) { list.push( <li> <div style={style.box}> <div>{this.props.posts[i].kakikomi}</div> <p style={style.name}>{this.props.posts[i].name}</p> </div> </li> ); } return ( <Layout title={this.props.title}> <div> <h2>{this.props.title}</h2> <ul style={style.ul}> {list} </ul> </div> </Layout> ); } } module.exports = Index;
up.jsx
var React = require('react'); var Layout = require('./layout'); var style = { textarea: {width:"100%", height:"60px"}, button: {marginTop:"30px"} } class Up extends React.Component { render() { return ( <Layout> <h2>投稿フォーム</h2> <form action="/up" method="POST"> <label>内容</label> <textarea name="kakikomi" style={style.textarea}></textarea> <label>名前</label> <input type="text" name="name" /> <button type="submit" style={style.button}>投稿</button> </form> </Layout> ); } } module.exports = Up;
やったこと
- layout.jsxでは外部ファイルstyle.css と 埋め込みを試しています
- その他はstyleをオブジェクトで定義し、各コンポーネント内に埋め込み
- スタイルのパラメータのハイフンを含むものはローワーキャメルケースにする必要がある。
ブラウザでの動作
- 前回の「node.js+Express+mongooseで掲示板を作ってみる」と全く同じになりました。
5. まとめ
- express-react-viewsをインストール
- app.js で jsx に express-react-viewsのエンジンを登録
- テンプレートをReactで書く
- スタイルをオブジェクトにしてjs内で定義・適用してみる