質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Babel

Babelは、JavaScriptの次世代仕様であるECMAScriptのコンパイラ。次世代の標準機能を用いて記述されたコードを、それらの機能に対応していないブラウザでも動作するコードに変換することができます。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

1回答

4252閲覧

react-rails + webpack + ES6 におけるサーバーサイドレンダリングについて

gesorein

総合スコア101

Babel

Babelは、JavaScriptの次世代仕様であるECMAScriptのコンパイラ。次世代の標準機能を用いて記述されたコードを、それらの機能に対応していないブラウザでも動作するコードに変換することができます。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2017/06/06 02:35

編集2017/06/06 02:54

Ruby on RailsでReact.jsを使用するため、react-railsのgemを用いて開発をしています。
また、JavaScriptでES6(ES2015)を使用するためにwebpackを使用しています。
webpackの設定は以下のとおりです。

JavaScript

1// webpack.config.js 2module.exports = { 3 entry: { 4 hello_world: __dirname + '/src/hello_world/components/hello_world.jsx', 5 }, 6 output: { 7 path: __dirname + '/../app/assets/javascripts/components', 8 filename: '[name].js' 9 }, 10 module: { 11 loaders: [ 12 { 13 test: /\.(js|jsx)$/, 14 loader: 'babel-loader', 15 exclude: /node_modules/, 16 query: { 17 presets: ["es2015", "react"], 18 } 19 } 20 ] 21 }, 22 resolve: { 23 extensions: ['.js', '.jsx'], 24 }, 25 externals: { 26 'react': 'React', 27 'react-dom': 'ReactDOM' 28 } 29};

##発生するエラーについて
react-railsにはサーバーサイドレンダリング(SSR)のための機能があり、
以下のように、prerender: truereact_component メソッドで
指定することによりSSRが可能になります。

<!-- index.html.erb --> <%= react_component('HelloWorld', {}, { prerender: true }) %>

React側のコードは以下のとおりです。

JavaScript

1// hello_world.jsx 2import React from 'react'; 3 4class HelloWorld extends React.Component { 5 render() { 6 return ( 7 <div> 8 <h1>Hello World!</h1> 9 </div> 10 ); 11 } 12}

しかし、これを実行すると以下のようなエラーが発生してしまいます。

Ruby

1React::ServerRendering::PrerenderError 2 3Encountered error "#<ExecJS::ProgramError: ReferenceError: HelloWorld is not defined>" when prerendering HelloWorld with {} 4eval (eval at module.exports ((execjs):20743:19), <anonymous>:1:1) 5Object.module.exports [as getConstructor] ((execjs):20743:19) 6Object.serverRender ((execjs):20934:31) 7...

このエラーが発生する原因は把握しており、
react-railsではSSRを実行するためにExecJSを使用しているのですが、
ExecJSは module.exportsrequire に対応していないためエラーが発生するようです。

babelでのトランスパイル時に module.exportsrequire が発生しないように
しようかと思いましたが、良い方法が見つかりませんでした。
もし、解決方法がわかる方がいらっしゃいましたら、教えていただけると幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

僕の記憶違いだと申し訳ないんですが、react-railsでSSRできる条件は以下が必要だった気がします。

1.app/assets/javascripts/components.js 内に記述してあるjsをSSR時に読み込みhtmlにして返す 2.SSRしたいコンポーネントはwindowオブジェクトに登録されている必要がある

以前僕が書いたreact-rails用のメモですがよろしかったら見てください!
react+rails+サーバーサイドレンダリングで開発してますよメモ①

投稿2017/06/20 04:01

MasakazuFukami

総合スコア1869

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gesorein

2017/06/21 07:51 編集

ご回答ありがとうございます! すみません、質問内容では省略していましたが、 app/assets/javascripts/components.jsは作成しており、以下のように記述しています。 //= require_tree ./components 以前、hello_world.jsxのファイル末尾に 「window.HelloWorld = HelloWorld;」を 記述していたのですが、「ReferenceError: window is not defined」という エラーが発生していたので、記述を削除しておりました。やはり必要なのですね。 どちらにせよwindowオブジェクトに登録しても、エラーでうまくいかないようです。 記事を拝見したところ「webpackを使用してES5形式まで落としている」と 記載されているのですが、webpackでなにか追加の設定が必要なのでしょうか? ご教示いただけると幸いです。よろしくお願いいたします。
MasakazuFukami

2017/06/21 08:52

すいません僕が使用していたときと色々変わっていました。 公式のリファレンスに書いてありますが、 ``` Your code can't reference document or window. Prerender processes don't have access to document or window, so jQuery and some other libs won't work in this environment :( ``` とりあえずprerender: trueの中ではwindowは参照できないですね。 また、ES6ままでよければ /app/assets/javascripts/componentの中に ```js window.HelloWorld = React.createClass({ render: function() { return <h1>hello world</h1> } }) // or class HelloWorld extends React.Component { render() { return <h1>hello world</h1> } } ``` の用な感じでそのまま入れて良いっぽいです。 つまり、グローバル変数にcreateClassしたものを入れるかES2015のまま入れる感じですね。 んーこれ結構闇がふかそうだなぁと思っていて、 ES2015のままだとprerenderできるけどブラウザが対応してないことがある ES5まで落とすとグローバル参照する方法がwindowしかないのに、execJSの中ではwindowオブジェクトはもちろんない(windowオブジェクトはブラウザのものなので) 結構八方塞がり感が。。。 react-rails自体を試してないので何とも言えませんが、rails5.1にはwebpackerと言うものがついているのでその機能をうまく使えば行けそうだなぁとも思ってます。 結論どうすればいいのかいまいちわからず終わってしまいました。。。 ちなみに、react-railsはサーバーサイドレンダリングに特化したものではなく、/app/assets/componentsに入っているreact componentをアセットパイプラインに入れてあげるよ的なものなのでサーバーサイドレンダリングに関してはそんなによくないかもしれないです。 しかもexecJSでreactComponentをhtmlにしたものを返すときにエラーが起きてもrailsがそのエラーを吸収してしまって上手くデバッグできないこともあります。。。 個人的にはrails + サーバーサイドレンダリング + reactであればhypernovaというgemが一番良いかと思います。 rails以外にもnodeのサーバーを建てなければいけないので少し面倒ですが。。。 すいません。。。 一応検証用にデモで作ったものを共有しておきます!(ユーザー登録が必要かもしれません) https://c9.io/fukaminmin/react-rails/
gesorein

2017/06/22 02:48

いろいろと試していただきありがとうございます。 react-rails + ES6の構成でのサーバーサイドレンダリングは、現状なかなか難しそうですね...。 Rails 4系で開発しているのでwebpackerはまだ試していないのですが、 こちらでやるとどうなるか試してみようと思います。 また、hypernovaというサーバーサイドレンダリングのためのgemがあるのですね。 非常に良さそうなので、nodeサーバーをたてて、ぜひこちらも試してみたいなと思います。 現状のところES6を使うなら「ほかの構成で開発する」という解決方法がベストかもしれません。 また良い解決方法が見つかったら、こちらに追記していこうと思います。 素晴らしいご回答ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問