LoginSignup
28
15

More than 5 years have passed since last update.

SinatraでReactを使うためのgemを書いた

Posted at

ReactJSが取り沙汰されるようになって久しい昨今、
Ruby界隈ではRails対応のgem、もしくはプラグインしか存在しておらず、
未だにSinatraやPadrinoでReactJSを(サーバサイドレンダリングも含め)使うためのgemがないことに絶望したので自分で書いた。

個人的にはサーバサイドレンダリングは筋が良い技術だとは到底思えないし、廃れゆくべき技術だと思っているが、
個人開発においてどうしても必要になったため、実装した次第である。

tl; dr;

namusyaka/react-sinatraというgemを書いた。
SinatraでReact.jsを用いたサーバサイドレンダリングができるようになる。

サンプルはnamusyaka/react-sinatra-sampleを参照。
このgemを動かすにあたって必要になるnamusyaka/react-sinatra-ujsというnpm packageも書いた。

考え方

やらないこと

react-railsが実装しているようないくつかの機能は、SinatraにSprocketsのようなデファクトスタンダードな実装がないことはもとより、Assetの運用はもはやwebpackでやらない理由がないため実装していない。

以下にやらないことを列挙する。

  • .jsxからjsへのtransformをしない
  • babelを用いるようなtranspileをしない
  • sprocketsやasset pipelineのサポートをしない

基本的にはwebpack(その実babelなんだろうけど)などを使ってコンパイルしたjavascriptをそのままサーバサイドのランタイムに食わせれば良い。

また、テンプレートファイルを用いるジェネレータも持っていない。

Sinatraにおけるアプリケーションの雛形はあってないようなものなので、不要。
Padrinoについてはpadrino recipesというものがあるので、そちらに今後追加する可能性はある。

使い方

基本的にreact_componentをどう使うか、という話になる。

:prerenderオプションなし (サーバサイドレンダリングをしない場合)

描画までの流れとしてはpadrino-helpersを使って、data-react-classdata-react-props属性を持つ要素を描画するだけである。
react-sinatraのconfigurationは全てがサーバサイドレンダリング向けの設定に終始するため、このケースだと小難しい設定は不要で、React::Sinatraregisterするだけでreact_componentヘルパーが使えるようになる。

class App < Sinatra::Base
  register React::Sinatra

  get ?/ do
    react_component('HelloApp', {})
  end
end

そうして描画した要素をクライアント側でnamusyaka/react-sinatra-ujsをロードすることで、ReactDOM.renderが該当要素を取得・描画していく。

:prerenderオプションあり (サーバサイドレンダリングをする場合)

こちらが本懐である。
あらかじめSinatraアプリケーションを起動するタイミングで、server上のJavaScript Runtime向けにビルドされたJavaScriptを読み込ませる必要があるなど、設定は多少面倒になる。
設定項目は次の通り。

設定項目

項目 内容 デフォルト
use_bundled_react react-sinatraにbundleされたreactjsソースコードをサーバサイドレンダリング時に使うかどうか false
env アプリケーションの環境。 ENV['RACK_ENV']
addon ReactJSのaddon機能を要するかどうか。これはuse_bundled_reacttrueの時以外関係ない。 false
asset_path サーバ上のJavaScript Runtimeが読み込むJavaScriptのコード。webpackなどでビルドしたファイルを読み込ませる。 nil
runtime サーバ上で動かすJavaScript Runtimeをsymbolで設定する。この記事を書いた時点では:execjsしか存在しない。 :execjs
pool_size JavaScript Runtimeを用いる際のConnectionPoolの設定値。 5
pool_timeout JavaScript Runtimeを用いる際のConnectionPoolの設定値。 10

ベースコードは次のようなイメージ。あくまでサンプルとして参考にしてほしい。

class App < Sinatra::Base
  register React::Sinatra

  configure do
    React::Sinatra.configure do |config|
      config.use_bundled_react = true
      config.env = ENV['RACK_ENV'] || :development
      config.addon = true

      config.asset_path = File.join('client', 'dist', 'server.js')
      config.runtime = :execjs
    end
  end

  get ?/ do
    react_component('HelloApp', {}, prerender: true)
  end
end

今後の展望

NodeJSをruntimeとして使えるようにする

therubyracermini_racerに代表されるruby界隈のJavaScript Runtimeはexecjs経由でとりあえずは使えるので、現状はexecjsしかサポートしていない。
が、フロントエンドエンジニア的にはサーバサイド上でもNodeでコードを実行できた方が何かと嬉しい気がしている。

例えばreact_on_railsではすでにそのような機能があり、サーバ上のnodeデーモンとunix domain socketを介して通信を行うことで、サーバサイドレンダリングを実現していたりする。
個人的にはnodeのデーモンをそのためだけに飼って、死活監視諸々の項目を抑えて面倒見るほどの嬉しさがあるのか、という点について懐疑的なので現状はサポートしていないが、後々そういった機能追加があるかもしれない。

運用実績を貯める

まだまだ個人サービスレベルでしか導入できていないが、今後は少し大きめのサービスのproductionにも突っ込んで様子を見てみたいと考えている。

おわりに

サーバサイドレンダリングにはいろいろと思うところがあるし、こういった技術が必要になるのは過渡期ならではなのではないか、とも思う。
なにはともあれ、せっかく作ったわけなので、Sinatra-erな方やPadrino Loverな方は是非、使ってみてほしい。

また、前職の同僚でJavaScripterである@jyane@about_hiroppyにはreact-sinatraを開発する上で多大な協力をいただいたので、この場を借りて感謝したい。
ありがとうございます。引き続きよろしくお願いします。

28
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
15