Code

2016年1月26日火曜日

React.js Vs AngularJS、どっちがいい その3 - React.js 速い!

TL; DR: Web App のスピードにこだわりがあるなら、React.js を使おう。
追伸:新しいプロジェクトが始まる場合、AngularJS 1.0 を使わないで、できれば、AngularJS 2.0 を待ったほうがいいと思います。

 その3で isomorphic (サーバー側レンダリング)を書こうと思いましたが、Reac.js のスピードに驚いたので、まず React.js について詳しくメモしておきたいです。
 今作ってる Single Page Web App のサイズがだんだん大きくなって、ライブラリを除いて、Dev バージョンはつい 15K 行を超えました。一般的に Framework を使うと、アプリのサイズは小さくなります。ちなみに、Dev バージョンの AngularJS 1.3 は約 25K 行、React.js (Flux) は 18K 行ぐらいあります。
  Web App の DOM Node は約 9500 で、メモリは 50M ぐらいを使っています。このサイズで、経験から、AngularJS はもう重くて、Dirty Check と DOM 更新は 400ms 超えることもあります。ユーザーは遅延が感じられるサイズになります。React.js の場合、サクサク動いてて、全然遅いと感じられません。
 Framework 自身のコストもありますので、AngularJS や、Backbone.js などはすぐ DOM を更新する系は重く感じます。React.js の Virtual DOM はかなり快適に動いていることがわかります。
 これは主に shouldComponentUpdate や React.addons.PureRenderMixin うまく使った結果だと思います。
 React.js の場合、Component の state や props が更新されると、render 関数がもう一度呼び出して、Virtual DOM を更新します。さらに更新された Virtual DOM と前の Virtual DOM を比べて、変更された部分だけ DOM を更新します。さっと見ると、AngularJS 1.0 の Dirty Check とあまり変わらないじゃないかと。でも、実際はかなり違います。AngularJS 1.0 は DOM 更新を随時に行います。最初も DOM をスキャンーして、Directive を処理します。メモリなどはかなりのサイズで消耗していきます。React.js の場合、Virtual DOM の比較アルゴリズムはうまく仮想を立てて、shouldComponentUpdate もうまく利用して、本来なら O(n3) ですが、O(n) で実現できるようになりました。つまり、一回の比較で、もう変更点がわかるようになります。Dirty Check は 一回の Digest で2回実行されます。世の中間違って AngularJS を使っているアプリの中に、多分無数の $timeout を使ってるなと思って、Digest はずっと動いているかもしれません。デスクトップはいいとして、モバイルの場合、電池の消耗は速いと思われています。
 
 でも Framework や Lib は使い方次第で、スピードがかなり異なりますので、今のチームでは、うまく PureRenderMixin を使って、state と props を比較して、Component の更新が必要ないなら、shouldComponentUpdate を false を返して、render 関数をスキップします。React.js の中にも false を見ると、その Tree を比較しないようになっています。下記のページもまとめています。
 react.js advanced performance

 Component には Lifecycle があります。なんでもかんでも setState() 関数を呼び出すと、react.js もさすがに重くなります。(前のチーム開発したApp はかなり重く感じられます。React.js でよかったなと思って、AngularJS の場合はもう重くて、重くて、使えないかもしれません。)
 だから、componentDidUpdate や、componentWillReceiveProps 関数の呼び出すタイミングを測って、どのように Parent Component を更新するかとか、新しい props をもらったら、どのように state を変更するかとか細かく考えないと。できるだけ Component 不要な render 関数呼び出しを減らさないと。
 それに、component は本当に state 必要かも考えましょう。必要ないなら、stateless の component を使いましょう。

 AngularJS の機能が強いから、いろんな使い方がありますので、簡単にはアプリ全体の構造が壊されます。($timeout を使って、次の Digest を呼び出して、DOM を更新するという間違った使い方。。。)
 React.js の場合、概念が簡単だし、機能はそんなに強くないから、逆にアプリ全体がきれいに見えます。

 もし、アプリのスピードに要求がある場合、React.js を使った方がいいと思われます。