2016年4月26日 東京Node学園付属小学校 2時限目
@cradle_of_nox
今日は React + Node で
Isomorphic Web Apps に挑戦した話をします
JavaScript applications that can run both on the client-side and the server-side.
クライアントサイドとサーバサイドの
両方で実行できる
JavaScript アプリケーション
Client
JavaScript
JavaScript
Server
Application
Client
Server
index.html
GET /index.html
GET /app.js
app.js
空のHTML
JS実行
DOM描画
遅延
JS 実行できない環境は ×
Client
Server
index.html
GET /index.html
GET /app.js
app.js
JS実行結果
と同じDOM
JS実行
DOM描画
早い
JS実行結果
と同じDOM
別の言語で
同じDOM
JS 実行できない環境もOK
Client
Server
index.html
GET /index.html
GET /app.js
app.js
JS実行結果
と同じDOM
JS実行
差分のDOMのみ描画
DOM描画
同じ言語で
同じDOM
import express from 'express';
import http from 'http';
const app = express();
app.use(express.static('public'));
app.set('view engine', 'ejs');
app.get('*', (req, res) => {
res.render('index');
});
const server = http.createServer(app);
server.listen(3003);
server.on('listening', () => {
console.log('Listening on 3003');
});
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
Hello, World!
</body>
</html>
server.js
view/index.ejs
import AppComponent from './components/app';
const routes = {
path: '',
component: AppComponent
}
export { routes };
import React from 'react';
export default class AppComponent extends React.Component {
render() {
return (
<div>
<h2>Welcome to my App</h2>
</div>
);
}
}
<body>
<%- markup %>
</body>
routes.js
components/app.js
view/index.ejs
import React from 'react';
import { renderToString } from 'react-dom/server';
import { match, RoutingContext } from 'react-router';
import AppComponent from './components/app';
import { routes } from './routes';
...
app.get('*', (req, res) => {
match({ routes, location: req.url }, (err, redirectLocation, props) => {
if (err) {
res.status(500).send(err.message);
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else if (props) {
// if we got props, that means we found a valid component to render
// for the given route
const markup = renderToString(<RoutingContext {...props} />);
// render `index.ejs`, but pass in the markup we want it to display
res.render('index', { markup })
} else {
res.sendStatus(404);
}
});
});
server.js
<body>
<div id="app"><%- markup %></div>
<script src="build.js"></script>
</body>
view/index.ejs
import React from 'react';
import ReactDOM from 'react-dom';
import { Router } from 'react-router';
import { routes } from './routes';
import createBrowserHistory from 'history/lib/createBrowserHistory';
ReactDOM.render(
<Router routes={routes} history={createBrowserHistory()} />,
document.getElementById('app')
)
client-render.js
var path = require('path');
module.exports = {
entry: path.join(process.cwd(), 'client-render.js'),
output: {
path: './public/',
filename: 'build.js'
},
module: {
loaders: [
{
test: /.js$/,
loader: 'babel'
}
]
}
}
webpack.config.js