【基礎】ReactでServer Side Rendering


概要

SSRの勉強をしたくて、Qiitaを漁ったら、
思いの外、他の方の記事がわかりづらかった(私の能力が低いだけです

Express + ReactでSSR

参考:React での ServerSideRendering 入門

準備

npm install express-generator -g
express myapp
cd myapp
npm install
npm install react react-dom node-jsx

npm start

サンプルコンポーネント

ただのReactコンポーネント。

components/sample.js
const React = require('react');

module.exports = class Sample extends React.Component {
    render(){
        return (
            <div>
                hoge
            </div>
        );
    };
};

Viewをブラウザに返す

node-jsxを入れておくと、requireでJSXを解釈してくれるので
入れておいたほうがいい。

Reactコンポーネントを読み込んで、
ReactDomServer.renderToStringでHTML文字にする。

あとはそれをブラウザに渡す。

app.js
・・・

require('node-jsx').install({harmony: true});

const React = require('react');
const ReactDomServer = require('react-dom/server');

var Sample = require('./components/sample');

const createHtml = component => (`
<html>
    ${component}
</html>
`);

・・・

//app.use('/', index);
app.use('/', (req, res, next) => {
    const sampleComp = ReactDomServer.renderToString(React.createElement(Sample));
    const view = createHtml(sampleComp);
    res.status(200).send(view);
});

・・・

今表示されているものは静的なHTML

のはず。

hogeを表示させるだけだとよくわからないので、
Componentのstateに、変化と、その表示をさせてみよう。

components/sample.js
const React = require('react');

module.exports = class Sample extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            count: 0
        };

        this.click = this.click.bind(this);
    }

    render(){
        return (
            <div>
                <input type="button" onClick={this.click} value="CountUP"></input>
                <div>{this.state.count}</div>
            </div>
        );
    };

    click() {
        this.setState({
            count: this.state.count + 1
        });
    }
};

うん、ボタン押しても動かない。

ブラウザでReactを動かす

あれじゃあどうやって仮想DOM使うんだ・・?

と調べると・・。
参考:hydrateでSSR後にブラウザでReactを動かす

マジカヨ。
結局、ブラウザ用にファイル用意するのね・・。

わしゃてっきり、webpackから解放されるものかと・・。

というわけでwebpack準備

npm install -D \
webpack webpack-cli \
babel-cli babel-preset-env babel-preset-react babel-loader

※"webpack": "^4.10.1"

webpack.config.js
const path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'public'),
        filename: 'index.js'
    },
    module: {
        rules: [
            {
                test: /\.(js)$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            'babel-preset-react',
                            'babel-preset-env'
                        ]
                    }
                }
            }
        ]
    }
};

肝は、express-generatorで作成したアプリなので、
デフォルトでpublicディレクトリが静的なファイルを置く場所と決まっていることだ。

なので、bundleファイルもそこに出力されるようにする。

表示の置換

参考を見る限り、
静的HTMLを、ReactのViewに後から置き換わるようにすれば良いみたい。

これは、ブラウザでReactを書いている人には馴染み深い
ReactDOM.render
とほぼ一緒だ。

renderではなく置換(という言葉であっているのか・・?)させる、
ReactDOM.hydrate
に変えてあげればいいみたい。

src/index.js(ブラウザ側Reactのエントリーポイント)
const React = require('react');
const ReactDOM = require('react-dom');
var Sample = require('./../components/sample');

ReactDOM.hydrate(
    <Sample />,
    document.getElementById('root')
);

bundleファイルを読み込みと、
DOMにレンダリング(置換)しないといけないので、最親のHTMLにコンテナを用意。

app.js
・・・

const createHtml = component => (`
<html>
    <div id="root">${component}</div>
    <script src="index.js"></script>
</html>
`);

・・・
bundleファイル作って、サーバ起動して・・
./node_modules/.bin/webpack
npm run start

動いた動いた。