Isomorphic Web Apps
2016年4月26日 東京Node学園付属小学校 2時限目
Kyrie
- Yuka Sato
- Gaiax Co.Ltd.
- HTML/CSS, Ruby, Rails
- React, Angular2, Node
@cradle_of_nox
今日は React + Node で
Isomorphic Web Apps に挑戦した話をします
Hello!
Isomorphic Web Apps?
JavaScript applications that can run both on the client-side and the server-side.
クライアントサイドとサーバサイドの
両方で実行できる
JavaScript アプリケーション
Node for Isomorphic
Client
JavaScript
JavaScript
Server
Application
Problem in SPA
Client
Server
index.html
GET /index.html
GET /app.js
app.js
空のHTML
JS実行
DOM描画
遅延
JS 実行できない環境は ×
SPA & Serverside Rendering
Client
Server
index.html
GET /index.html
GET /app.js
app.js
JS実行結果
と同じDOM
JS実行
DOM描画
早い
JS実行結果
と同じDOM
別の言語で
同じDOM
JS 実行できない環境もOK
Isomorphic!
Client
Server
index.html
GET /index.html
GET /app.js
app.js
JS実行結果
と同じDOM
JS実行
差分のDOMのみ描画
DOM描画
同じ言語で
同じDOM
Example
Creating A Server
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
Building The React App
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
Serverside Routing
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
Clientside Rendering
<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
Generating build.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
Feeling
- これで SPA の初期ロード時間・SEO問題が解決!最高!
- ...という訳にもいかなさそう
- クライアントで実行するコードを Node と共有するため、ある程度は抽象化する必要が出てくる
- window とか document のようなクライアント特有のコードは、退避させたりする必要があるっぽい
- モジュールを使う場合は、isomorphic に対応してるかどうか確認した方がいいっぽい
-
とにかく前提知識が多くてつらくなる
- react, redux, react-router, webpack, node, express, isomorphic-fetch, ...
Feeling
- 「Isomorphic は辞めた方が良い」と言ってる人もいる
- Web だけではなく、ネイティブアプリやデスクトップアプリもクライアント
- より良い UX のためには、プラットフォーム特有の機能を使う必要がある
- すべてのプラットフォームで一つのコードを共有するのは、多くのハッキングを必要とするし、管理も困難 by Say No to Universal Apps
-
SSR をするのであれば Node が絶対ラクだし必須、でも、おすすめできるかというと...
- 思っていたよりは簡単だった、でもクライアント特有のコードをバリバリ使うならツラそう...
- Google が JS を認識してくれる今、本当に SSR が必要なのか...
Feeling
- しかしながら、夢とロマンがある
- isomorphic 可能な小さなモジュールが作られていけば、npm のエコシステムは加速する
- Web, Native, Desktop, IoT... 色んなプラットフォーム上で Node が動く、同じコードが実行される未来が来たら最高
Happy Hacking!
Isomorphic Web Apps
By Sato Yuka
Isomorphic Web Apps
2016年4月26日 東京Node学園付属小学校 2時限目 で発表したスライドです。
- 2,219