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