2019年03月16日

ReactでSSR

実験的にReactでSSRを行っていく。まずはローカルでExpressサーバーを立ててSSRを行う。

まずは普通にReactでページを表示



まずはクライアントのJS用に以下のコマンドで各種ツールをインストール

npm init -y
npm install --save-dev webpack
npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/preset-react
npm install react react-dom


package.jsonのscriptsにはビルド用のコマンドを設定、babelの設定も追加しておく

  "scripts": {
"build": "webpack --mode development",
"start": "webpack --mode development -w",
"release": "webpack --mode production"
},
"babel": {
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
},



webpack.config.jsを作成

const path = require('path');

module.exports = {
entry: "./src/script.js",
output: {
path:path.resolve(__dirname, "dist"),
filename: "script.js"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
}
};


src/App.jsにAppコンポーネントを定義して

import React from 'react';

export const App = () => <h1>Hello React</h1>


src/script.jsにAppコンポーネントを出力するReactを書いて

import React from 'react';
import ReactDOM from 'react-dom';
import { App } from 'App';

ReactDOM.render(
<App />,
document.getElementById('app')
);




dist/index.htmlを設定

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script src="./script.js"></script>
</body>
</html>


これでnpm run buildしてdist/index.htmlを表示するとhello worldが書き出されています。

サーバーの準備



次にサーバーの準備をしていきましょう。今回は後でSSRするためにnode.jsのサーバーExpressを用意します。

まずはExpressのインストール

npm install express


サーバー用の設定ファイルserver.jsを作成します。

'use strict'

const express = require('express')
const app = express()

app.use(express.static('dist'));

app.listen(3000)


dist/index.htmlがルートディレクトリになるように設定します。

ターミナルから以下のコマンドを打つと

node ./server.js


localhost:3000でHello React!が表示されます。

サーバーサイドでSSR



ここからが本題、サーバーサイドでSSRを行っていきます。

まずはJSXやES2015が解釈できるようにserver.jsをWebpackでコンパイルするようにします。

Webpackでnode.jsが扱えるようにwebpack-node-externalsをインストールして

npm install --save-dev webpack-node-externals


サーバー用の設定ファイルを以下のように定義します。

const path = require('path');
const nodeExternals = require('webpack-node-externals');

module.exports = {
mode: 'production',
entry: "./server.js",
target: 'node',
output: {
path:path.resolve(__dirname, ""),
filename: "./server.dist.js"
},
externals: [nodeExternals()],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
}
};


これで./server.jsの内容をコンパイルしてserver.dist.jsに出力できるようになります。

package.jsonのscriptsに以下のコマンドを追加しておくと

  "scripts": {
・・・
"server": "npm run build:server && node ./server.dist.js",
"build:server": "webpack --config webpack.config.server.js"
},


npm run serverのコマンドでスクリプトのビルドとサーバーの起動をやってくれます。

server.jsは次のように変更します。

import  express from 'express'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import fs from 'fs'
import path from 'path'

import { App } from './src/App.js'

const app = express()

app.use(express.static('./dist'));

app.get('/', (req, res) => {
fs.readFile(path.resolve('./dist/index.html'), 'utf8', (err, data) => {
return res.send(
data.replace(
'<div id="app"></div>',
`<div id="app">${ReactDOMServer.renderToString(<App />)}</div>`
)
)
})
})

app.listen(3000)


ReactDOMServer.renderToString()でAppコンポーネントの内容がHTMLに変換されて<div id="app"></div>内に挿入されます。

書き出されるHTMLはこんな感じに。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app"><h1 data-reactroot="">Hello React</h1></div>
<script src="./script.js"></script>
</body>
</html>


ひとまず今回はここまで。次回はルーティング周りの対応を入れていきます。
posted by ねこまんま at 11:48 | React | 更新情報をチェックする