Create React App で SSR してみた。

Create React App で SSR してみた。

どうも fujihara です。あけましておめでとうございます。今年もよろしくおねがいします。 本日は勉強がてら Create React App で初回に表示されるものを Server Side Rendering する方法をご紹介します。

はじめに

Create React Appでプロジェクト作ると、いい感じの開発環境できるんですが、 サーバサイドレンダリングはしてくれていないので、どうにかできないかと思って 初期段階のもので設定してみました。 webpack.config.js などの設定は要リファクタリングです。 また自動生成された src 配下は極力いじらないようにしてあります。

必要モジュールのインストール

Create React App した時点でかなりのモジュールがインストールされているので必要な分だけインストールします


$ yarn add webpack-cli express nodemon node-style-loader webpack-node-externals babel-loader@8.0.4

2019/01/17時点で babel-loaderは 8.0.4 じゃないとwebpack 実行時にエラーが出ます。エラーが出た場合は指定された babel-loader をインストールし直してください。

ディレクトリ・ファイルの追加


dist クライアント用jsの出力先
server サーバー用jsの出力先
src/server.js サーバー用js
webpack.config.js  webpack設定ファイル

追加ファイル

webpack.config.js クライアントとサーバ用のもの設定してあります。


const webpack = require('webpack')
const path = require('path')
const webpackNodeExternals = require('webpack-node-externals')

module.exports = [
  {
    output: {
      path: path.resolve("dist"),
      publicPath: "/",
      filename: "build.js"
    },
    entry: "./src/index.js",
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          use: {
            loader: "babel-loader",
            options: {
              presets: ['@babel/preset-env', '@babel/preset-react']
            }
          }
        },
        {
          test: /\.css$/,
          use: ['style-loader', 'css-loader']
        },
        {
          test: /\.svg$/,
          //img タグで srcプロパティに直接 import し代入できる
          use: ['@svgr/webpack', 'url-loader']
        }
      ]
    }
  },
  {
    output: {
      path: path.resolve("server"),
      filename: "server.build.js"
    },
    entry: "./src/server.js",
  //以下2行 node 用宣言
    target: 'node',
    externals: webpackNodeExternals(),
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          use: {
            loader: "babel-loader",
            options: {
              presets: ['@babel/preset-env', '@babel/preset-react']
            }
          }
        },
        {
          test: /\.css$/,
          use: [
            // node用
            'node-style-loader',
            'css-loader'
          ]
        },
        {
          test: /\.svg$/,
          //img タグで srcプロパティに直接 import し代入できる
          use: ['@svgr/webpack', 'url-loader']
        }
      ]
    }
  }
]

server.js


import express from 'express'
import React from 'react'
import App from './App'
//読み込んだコンポーネントを出力用に変換
import { renderToString } from 'react-dom/server'
//スタイルを取得
import { collectInitial } from 'node-style-loader/collect'
import './index.css'

const app = express()

//静的ファイル
app.use(express.static('dist'))

app.get('*', (req, res) => {
  const markup = renderToString(<App/>)
  //style 取得 tagで取得できる
  const initialStyleTag = collectInitial()
  // html 出力 (markup , initialStyleTag, クライアント用js )
  res.send(`<html>
<head>
<title>ssr</title>
${initialStyleTag}
</head>
<body><div id="root">${markup}</div><script src="/build.js" defer></script></body>
</html>
`)
})

app.listen(process.env.PORT || 3000)

src/index.jsの修正


import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
// サーバ内で出力されたスタイルタグを消す
import serverStyleCleanup from 'node-style-loader/clientCleanup'

ReactDOM.hydrate(<App />, document.getElementById('root'));
//SSRされたstyleタグを削除
serverStyleCleanup()

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();

実行

package.jsonの scripts に "webpack -w --mode development & nodemon server/server.build.js " を登録してあげると楽です。 localhost:3000 で確認できます。以下出力された html ソースです。レンダリングされていますね。

SSR-ソース.png

まとめ

個人的には後からSSRに設定するよりは最初に設定してしまう方が好みです。 実際はここから色んなカスタマイズが待ってるんですが、参考になれば良いです。 まあ、 Next.js, React Starter Kit 使ってって話ですかね。

  • このエントリーをはてなブックマークに追加

この記事を読んだ人にオススメ