10月なのにまだ若干半袖にしようかどうかで迷っています。それでも最近はまだましですが・・・。
さて、去年と比べて最近はReact.jsが大分話題にあがるようになってきたようです。Facebook(正確にはInstagramだったっけか)で実際に利用されている、という点も、実績という意味では十分になってきたから、ということでしょう。
JSXという仕組み自体にはまだ賛否両論があるのを見たことがあります。確かに完全に独自の仕組みなので、Shadow DOMとかとは組み合わせるのはできなさそうです。
しかし、Polymerとかと比較して、本当にView側に特化したライブラリなので、ロジック側とか周囲についてはすべて自由に選択できる、というのは結構ポイントだと思います。
個人的にFacebookは好きでも何でもないのですが、DocumentFragmentと戦うことなく、スマートに実装できるんであればいいんじゃない?という感じではあったので、いつも通り現場でこっそり導入してみました。
導入した環境とツール類
とりあえず導入するにあたって、以下ができることを前提にしました。
- requireの方法は基本変えない
- webpackのloaderを利用していますが、この使用方法とかをメンバーに教えたりするのが非常にめんどいので、何も知らなくても使えるように。
- 変換は全部一括
- テストケースでもJSXが利用できるように。
- JSXのソースでもjshintしたい。
- Reactを利用したソースは.jsx、React + CoffeeScriptなソースは.cjsxの拡張子で扱えるように
で、現在の環境はこんな感じです。
- ビルドツールはGulp + Webpack
- Backbone.js ( + 一部だけMarionette、さらに全体の半分くらいはグローバルごりごり)
- HTMLはすべてassets/templatesみたいなディレクトリにすべて集めてます
- テストケースはCoffeeScriptでも書けるように
- 実際はプロダクトコードも書けるようになってますが、説明が(以下略
上述のことをできるようにするため、以下のツールを導入しました。
- jsx-loader https://www.npmjs.org/package/jsx-loader
- jsxhint https://www.npmjs.org/package/jsxhint
- coffee-reactify https://www.npmjs.org/package/coffee-reactify
設定として若干ポイントだったのは以下の点でした。
- coffee-reactify は、 preLoadersの方に設定すること
それ以外は特に問題なく、わりとさっくり導入できました。ただ、React.jsはnpmでinstallするのがオススメです。
bower経由だと、Webpack時に警告が毎回出てしまうので、普通にnpmしておくのがオススメです。
React.js関連のライブラリ
React.jsとBackbone.jsを連携させるライブラリはいくつかあります。
react.backbone https://github.com/clayallsopp/react.backbone
react-backbone-component https://github.com/magalhas/backbone-react-component
どちらも活発さ加減は大分違いますが、react-backbone-componentはあくまでMixinにこだわり、react.backboneはもう少しシンプルに扱うためにReact本体に直接メソッドを生やしている点が大きな違いのようでした。
今回はとりあえずシンプルに進めるため、react.backboneを利用しています。
React.jsとBackbone.jsを実際に組み合わせる
React.jsは当然Viewなので、React.jsを使う時点で、Backbone.Viewは用がなくなります。
ただし、一気に全部作り直すのはぶっちゃけ無理なので、ボトムアップで作り直ししていってます。
実際にReact.jsとBackbone.jsを組み合わせるとき、個々のViewでは、Backbone側のコンポーネントとしてはBackbone.ModelとBackbone.Collectionを主に利用します。
react.backboneは、再レンダリングを行うイベントへのバインディングとかをサポートする機能があるので、これをちょっと利用したりできます。
で、実際に書いてみると、それぞれのモジュールは、よっぽどシンプルなものでなければ、以下のようになります。
var T = React.createBackboneClass({
render : funciton() {
return <span class="hoge">{this.getModel().get('foobar')}</span>;
}
});
module.exports = React.createBackboneClass({
render : function() {
return (
<div>
<h5>title</h5>
<T model={this.getModel()} />
</div>
);
}
});
renderの中に多数の要素を含んでしまうと、逆に見づらくなってしまうので、できるだけ細かいコンポーネント単位で分割していった方がよさそうです。
一部だけ作り直した場合、このモジュールを別のBackbone.ViewやMarionette.ItemViewとかで利用する場合、以下のような感じになりました。
var S = require('app/view/s');
module.exports = Marionette.ItemView.extend({
onRender : function() {
React.renderComponent(S({model : this.model}), this.el);
}
});
必ずrenderComponentを実行しないとなりません。多少複雑なViewの中なのであれば、LayoutViewにして、 getRegion('..').show().el
として、この要素に対してrenderComponentすることで、大体問題なくなります。
React.jsとテスト
さて、Viewのユニットテストはコストが半端ないのであまり書きたくないところですが、簡単なclassのつけはずしくらいならさっと書いてみたいものです。
React.jsには、標準のaddonsとして、TestUtilsというaddonが用意されています。
これが普通に使えるので、正直テストケースでもjQueryを使う必要性はあまりなくなってきたと思います。
また、ビルドツールとしてWebpackを利用しているので、karma側では別段特別な設定はやっていません。
Karmaのpreprosessorとかは、いろいろあるので使っていません(主にスペック的な問題で)。
一回のビルドあたり大体10秒かかるので、まぁこの辺は若干の妥協です。
ただし、このユーティリティの中にある scryRenderedComponentsWithType
という関数ですが、前述したモジュールの書き方だと、これを利用できません。
まぁ、外に見えないコンポーネントなのであれば、別段問題はないとは思います。ただ、内部の構造自体に依存してしまっていると、内部だけちょっと変更された場合にテストケースを修正したりしないといけないので、できるだけ分割して、上のTestUtilsの中でも、〜WithTypeとかを利用するようにした方がいいんじゃね?と思います。
ただ、上のような単純な構造であれば、普通にrefをつければとれるので、そっちでやっても全く問題はないと思います。
そのほかの仕組み
今のところ、Marionette.ApplicationやAppRouterを利用しているのはごく一部なのですが、その辺の仕組みは特に変更する必要はないです。
React.jsを導入したときの問題
ビルドツールとかを利用しているんであれば、設定を行う人がちょっとがんばればすぐ利用できますし、Backbone.jsのView以外には特に影響しないので、こちらもあまり問題にはならないと思います。
ただ、以下のような問題はひたすらつきまといます。
- React.ComponentをBackbone.Viewの要素に追加した後、Backbone.View側からDOMを直接いじってしまう
- これをやるとアウトです。なので、Backbone.Viewを使う場合、完全に DOM以外のみを管理するようにしないと、レンダリングする際にエラー吐かれます。
- つまりはjQueryとおさらばする覚悟があるかどうか、という感じです。私はjQueryだけで書かれた数万行のJavaScriptとかもう見たくもないので大丈夫でしたが。
- また、元の要素に対して追加・削除するようなプラグインも当然ながら使えません。bodyにだけ新しい要素を作るようなものならばうまくいくかもしれませんが。
- エディタ・IDEの問題
- 私はいろいろあってVagrant上にEmacsを導入して、その上でjs2-mode + web-modeを使ってわりかし快適なんですが、ほかのメンバーが入ってくる関係上、Eclipse(またはIntelliJ)を外すことができません。一応サポートはしているようなので大丈夫だとは思いますが。
- 教える難易度
- メンバーのレベルにもよりますが、現在React.jsは日本語のまとまった情報とかはなく、基本的に「ググれ」かつ「英語読め」以外なんともしようがない状態です。ほかのメンバーもそれが苦もなくできるレベルならいいんですが、できない・期待できない場合、採用を見送るか、もしくはそれらのメンバーに対して「こうすればいいから!」と言い切れるだけのライブラリセットを用意する必要があると思います。
- ちゃんと分離することの重要性
- React.jsでは、それぞれの要素単位で細かく分けることが重要です。これをやらないとあまり意味がないです。
- 特に、 Backbone.Viewのrenderを巨大にしてしまうような人 がいる場合、悪夢再来になるので、ちゃんとレビューするとかしないと、結局新しいカオスが生まれます。
これらの、決して小さくはない問題を鑑みた場合に、作り直したりする作業量に見合うかどうか、というのも考える必要があると思います。
特に業務系だと、Date Picker系統のjQueryライブラリは必携だったりするので、その辺が使えるかどうかだけでも確認しておいた方がいいと思います。
ただ情報を表示するだけでいいような部分であれば、Reactにしておいた方が後々シンプルになるとは思います。template/JavaScriptでそれぞれ行ったり来たりしなくてよくなるので。
現状の結論
とりあえず、現状ではReact.jsを導入しても問題ないといえるのは、 ちゃんとJavaScriptがわかっている人かつ、 英語でも普通に調べられる人 がメンバーの大勢を占めているようなメンバーか現場でしょう。
それ以外はかなりリスキーだと思います。特に、Backbone.jsを使っているような現場の場合、Backbone.jsのViewとは使い方も何もかもが違うので、その辺が変更することに対する拒絶反応とかもあると思います。
特にプロダクションの場合、あるjQueryプラグインに強く依存してしまっていてどうしようもない、という状態もあると思うので。
私はもう導入してしまったということもあるので、できるだけ利用箇所を広げていって、ノウハウをためていこうかと思います。
こんどはClojureScriptとかTypeScriptとかHaxeとか導入したいなー(反省なし)