Edited at

RailsとReact + Webpackでサーバサイドレンダリング

More than 3 years have passed since last update.

WebpackでReactをビルドしたものをRailsでサーバサイドレンダリングする方法をまとめました。


Webpack側

$ npm i -D webpack babel babel-loader babel-core

$ npm i -D babel-preset-react babel-preset-es2015

Reactのコンポーネントのテストを書く場合はreactreact-domをインストールする必要があります。

そうでない場合はインストールしなくても、react-railsにこれらが含まれているので問題ありません。


Webpackの設定

Reactのコンポーネントをreactディレクトリに配置して、ビルドしたものはapp/assets/javascripts/componentsに出力されるようにします。

reactreact-domはreact-railsのものを利用するので、externalsに設定しておきます。


webpack.config.js

const DEBUG = !process.argv.includes('--release');

module.exports = {
cache: DEBUG,
debug: DEBUG,

entry: {
Hoge: './react/Hoge.jsx',
},
output: { path: './app/assets/javascripts/components', filename: '[name].js' },
devtool: DEBUG ? 'inline-source-map' : false,
module: {
loaders: [
{
test: /.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['es2015', 'react']
}
}
]
},
resolve: {
extensions: ['', '.js', '.jsx'],
},
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
},
}



Reactのコンポーネント

Reactのコンポーネントは以下のように、サーバサイドレンダリングするオブジェクトをwindowのプロパティとして出力します。


react/Hoge.jsx

import React from 'react';

class Hoge extends React.Component {
render() {
return (
<div>
hogehoge
</div>
)
}
}

window.Hoge = Hoge;



package.json

package.jsonにはscriptを設定します。

開発する時はnpm run buildnpm run watchコマンドを使うといいと思います。


package.json

{

"scripts": {
"build": "webpack",
"watch": "webpack --watch",
"release": "webpack --release"
},
}


Rails側


react-railsをインストール


Gemfile

gem 'react-rails'


$ bundle install

$ bundle exec rails g react:install

config.react.variantを設定


config/environments/development.rb

 Rails.application.configure do

+ config.react.variant = :development
end


config/environments/production.rb

 Rails.application.configure do

+ config.react.variant = :production
end


production環境の設定

server_rendering.jsを新たに作成します。


app/assets/javascripts/server_rendering.js

//= require react-server

//= require components

それぞれのファイルに追記します。


config/initializers/assets.rb

Rails.application.config.assets.precompile += %w( server_rendering.js )



config/environments/production.rb

 Rails.application.configure do

config.react.variant = :production
+ config.react.server_renderer_options = {
+ files: ["server_rendering.js"],
+ }
end

Precompile時にWebpackでビルドするために、以下のファイルを新たに作成します。


lib/tasks/before_precompile.rake

task :build_react do

sh "npm run release"
end

Rake::Task["assets:precompile"].enhance(%i(build_react))



Reactのコンポーネントのレンダリング

react_componentに表示したいコンポーネントと、引数を渡します。

題3引数に{prerender: true}を渡してサーバサイドレンダリングを有効にしています。


index.html.erb

<%= react_component('Hoge', {}, {prerender: true}) %>



おわりに

今回編集および追加したファイルは以下のようになっています。

rails_root

├── app/
│ └── assets
│   └── javascripts
│      ├── application.js
│         ├── components
│         ├── components.js
│         └── server_rendering.js
├── config/
│   ├── environments
│   │   ├── development.rb
│   │   └── production.rb
│   └── initializers
│      └── assets.rb
├── lib/
│   └── tasks
│   └── before_precompile.rake
├── react/
│   └── Hoge.jsx
├── Gemfile
├── package.json
└── webpack.config.js

app/assets/javascripts/componentsはビルドするたびに変更が発生するので、gitを使って複数人で開発しているとコンフリクトしやすいです。

gitignoreにapp/assets/javascripts/components以下のファイルを無視するように設定しておくべきかもしれません。


参考