前提条件
- 自分用メモ
- React v16
- Node.js v8.2.1
本文
まずは公式通りにプロジェクト作成。そして必要なものをインストール。
$ npm install -g create-react-app # 既にある人は無視して
$ create-react-app easy-ssr
$ cd easy-ssr
$ npm install
$ npm install -S express react-dom node-jsx babel-register babel-preset-es2015
さて、今回はただSSRを試すだけなのでsrc
の中はそのままに、create-react-app
で生成されたものをそのまま利用します!
ちなみに僕は同時進行でやりながらクライアントとサーバーのソースの書式が違うのが気持ち悪いのでbabel-registerとbabel-preset-es2015を入れておきます。これでサーバーサイドでも心置きなくES2015で書けますね!
そして.babelrc
を作っておきます。
$ vi .babelrc
{
"presets": ["es2015", "react"]
}
今回はシンプルにReactDOMServerを使います。
Reactのrender結果をサーバー上で作ってくれるやつですね。先ほどのプロジェクト作成でもう既にインストールされています。
続いてサーバー用のファイルを作ります。
$ touch server.js
そいでpackage.json
を弄ります。
$ vi package.json
サーバーのスタートコマンドのserver
をscripts
に足しておきます。
{
"name": "easy-ssr",
"version": "0.1.0",
"private": true,
"dependencies": {
"babel-preset-es2015": "^6.24.1",
"babel-register": "^6.26.0",
"express": "^4.16.2",
"node-jsx": "^0.13.3",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-scripts": "1.0.17"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"server": "IS_SERVER=true node -r babel-register ./server.js" #これを追加
}
}
これでnpm run server
でES2015のNode.jsが動きますね!(なにやら環境変数ありますがそれは後ほど登場します)
さて、早速server.js
を書いていきます。
import fs from 'fs';
import express from 'express';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import NodeJsx from 'node-jsx';
NodeJsx.install({harmony: true});
import Html from './src/Html';
import App from './src/App';
import asset from './build/asset-manifest.json';
const initialData = {
logo: `/build/${asset['static/media/logo.svg']}`
};
const app = express();
const PORT = process.env.PORT || 3000;
app.use('/build', express.static('build'));
app.get('/service-worker.js', (req, res) => {
res.end(fs.readFileSync('./build/service-worker.js'));
});
app.get('/', (req, res) => {
res.status(200);
res.setHeader('Content-Type', 'text/html');
res.end(
ReactDOMServer.renderToStaticMarkup(
<Html asset={asset} initialData={JSON.stringify(initialData)}>
<App {...initialData} />
</Html>
)
);
});
app.listen(PORT, () => {
console.log(`Server running at port: ${PORT}`);
});
ほんと無理やりですが./build/asset-manifest.json
からビルド後のファイル名を取得してしまいます。さらには/service-worker.js
はただプロキシしているだけです。我ながらすごいやり方
次に./src
の中にHtml.js
を追加します。
import React, { Component } from 'react';
class Html extends Component {
constructor(props) {
super(props);
}
render() {
return (
<html>
<head>
<title>App</title>
<link rel="stylesheet" href={`/build/${this.props.asset['main.css']}`} />
</head>
<body>
<div id="root">{this.props.children}</div>
<script id="initial-data" type="text/plain" data-json={this.props.initialData}></script>
<script src={`/build/${this.props.asset['main.js']}`}></script>
</body>
</html>
);
}
};
export default Html;
さらに./src/App.js
を修正します。
import React, { Component } from 'react';
if (process.env.IS_SERVER !== 'true') {
require('./App.css');
require('./logo.svg');
}
class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={this.props.logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
);
}
}
export default App;
読み込んでないとbuildディレクトリにビルドしてくれないのでApp.css
とlogo.svg
の読み込みは必須です!でも、サーバーで起動するときにこれがあるとエラーにしかならないので環境変数でサーバー起動時には読まないように小細工...なんか多分すごい悪手だ
最後に./src/index.js
も弄ります。
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
const initialData = JSON.parse(document.getElementById('initial-data').getAttribute('data-json'));
ReactDOM.hydrate(<App {...initialData} />, document.getElementById('root'));
registerServiceWorker();
...さて準備が終わったところで、buildファイルを生成します!
$ npm run build
./build
ディレクトリができましたね! ではサーバーを起動してみます。
$ npm run server
> easy-ssr@0.1.0 server /Users/user/git/study/easy-ssr
> IS_SERVER=true node -r babel-register ./server.js
Server running at port: 3000
早速ブラウザでアクセス!
しっかり表示されていますね!ソースも確認してみましょう!
わーいやったぞー!!!SSRされた生なソースが来てますね!生っ!!
...今回できたゴミは一応下に貼って起きますね。。
https://github.com/kokoyoshi/easy-ssr