React.js を使うと、よくある DOM node に CSS クラスだけを設定したり、その DOM node の高さを変えたいときがあります。
DOM 操作をする場合、componentDidMount、と componentDidUpdate 二つ箇所を考えなければなりません。最初 Component が render する場合、React.js は Virtual DOM を作成して、実際の DOM に追加されたら、componentDidMount 関数が呼び出されます。その中に、findDOMNode(this) を使ったり、this.refs.REF を使って、DOM node へのアクセスは可能です。また、component に新しい props が設定されたり、setState を読んだりすると、componentDidUpdate が最後に呼び出されます。Mount される時と同じ、DOM nodeを取得して、操作できます。
親と子の関係について、まず子の didMount と didUpdate 関数が呼び出されて、最後に親の関数が呼び出されます。
もし親に CSS クラスを追加する場合、setState を使うと、全 App の子 Component が re-render するので、スピードだけを比較すれば、直接 DOM を操作するほうが速いかもしれません。ただ、DOM 操作を直接やると、Virtual DOM と実際の DOM が合わなくなるので、一回やると、もう関係ある DOM の更新を全て自分でしなければなりません。App 全体の構造から見ると、それは良くないです。オススメできないです。(自分の経験から見ると、逆に面倒です。)
この場合、shouldComponentUpdate 関数を使ったほうがいいです。まず、各 Component を作るとき、どのような props または state 値変更があったら、更新すべきかを考えたほうがいいです。その値の比較を shouldComponentUpdate の中に書いて、例えば:
return this.props.PROP !== nextProps.PROP || this.state.STATE !== nextState.STATE;
そうすると、親が setState を自分の state を更新して、子 Component の PROP が関係ない場合、その子の shouldComponentUpdate が false を返して、Virtual DOM の比較が行われなく済みます。
React.js はこのような DOM 操作をまとめてします。また Browser もそのような処理が入っていて、できるだけ画面更新を一回でやりたいです。個別の DOM 操作と比べると、そんなに遅くないです。
React.js も pure render mixin が提供しています。もし自分で shouldComponentUpdate を書きたくない場合、その mixin を使えば、同じ効果が得られます。(props で関数を子 Component 渡す時、関数の Bind を使わないように。なぜなら、bind は毎回新しい関数が返されますので、reference が更新されて、pure render mixin はいつも true となります。)
AngularJS が最初に出た時、使い方として、directive に DOM 操作すべきとか Think in AngularJS way がありました。 React.js も同じように、できるだけ、React.js にいろんな操作をやらせるべきです。
Think in React.js way です。
それでは。
Code
2016年5月22日日曜日
2016年5月1日日曜日
React.js Web App アーキテクチャー
React.js を使うと、よく変数は Component の props により親からセットするか、State に入れて、操作するかと相談されます。また、アプリ全体として、どのように作ればいいかと度々聞かれます。
今まで作った React App を例として:
DOM に Render するとき <ExampleApp {...props} />
ExampleApp の中に store からデータを取得して、state として保持。
App 中の Component はできるだけ props を使って、データまたは callback 関数をセット。
子 Component の中に、props を使って、データを表示したり、親の callback を読んだりする。
必要であれば、子 Component は state も保持する。
理由としては、React.js で Component を更新する場合、props をセットしなおす、setState({newstate}) 関数読む二つ手段があります。ExampleApp に store のデータを state に保存しているので、データが変わったら、App の setState を読んで、render 関数がもう一度呼び出して、新しい props がセットされるため、すべての子 Component が Re-render して、更新します。また子 Component 自分の state を更新する場合、子 Component の render 関数だけ呼ばれますので、App 全体のリセットが行われなくて済むので、速いです。
注意点として、まず、props が使えるなら、props を使うべきです。state の場合、re-rendering が発生する場合、保持されますので、使うときは気をつけないと。
次、props は readonly であるべきです。props は親から渡されるので、object の 場合、reference なので、もし間違って変更したら、親持ってるデータも同時に変更されますから、次 render する場合、props が変わってないようになったら、Component が更新されない可能性が高いです。
子 Component から、親に onClick や、他の Component を更新すべきと通知する場合は、親からの callback を呼ぶべきです。
onClick: function (e) { /* 子 Component 自分の操作 */
this.props.parentCallback(e); }
では、何を子 Component state に保持すべきかとういうと、例として checkbox とか form control など。その state は自分だけ使う、他の Component はそれを知らなくていいものです。もし同階層の他の Component がその値に従って、変更する場合は、親に state を保持して、props として、二つ Component に渡すべきです。
それでは。
今まで作った React App を例として:
DOM に Render するとき <ExampleApp {...props} />
ExampleApp の中に store からデータを取得して、state として保持。
App 中の Component はできるだけ props を使って、データまたは callback 関数をセット。
子 Component の中に、props を使って、データを表示したり、親の callback を読んだりする。
必要であれば、子 Component は state も保持する。
理由としては、React.js で Component を更新する場合、props をセットしなおす、setState({newstate}) 関数読む二つ手段があります。ExampleApp に store のデータを state に保存しているので、データが変わったら、App の setState を読んで、render 関数がもう一度呼び出して、新しい props がセットされるため、すべての子 Component が Re-render して、更新します。また子 Component 自分の state を更新する場合、子 Component の render 関数だけ呼ばれますので、App 全体のリセットが行われなくて済むので、速いです。
注意点として、まず、props が使えるなら、props を使うべきです。state の場合、re-rendering が発生する場合、保持されますので、使うときは気をつけないと。
次、props は readonly であるべきです。props は親から渡されるので、object の 場合、reference なので、もし間違って変更したら、親持ってるデータも同時に変更されますから、次 render する場合、props が変わってないようになったら、Component が更新されない可能性が高いです。
子 Component から、親に onClick や、他の Component を更新すべきと通知する場合は、親からの callback を呼ぶべきです。
onClick: function (e) { /* 子 Component 自分の操作 */
this.props.parentCallback(e); }
では、何を子 Component state に保持すべきかとういうと、例として checkbox とか form control など。その state は自分だけ使う、他の Component はそれを知らなくていいものです。もし同階層の他の Component がその値に従って、変更する場合は、親に state を保持して、props として、二つ Component に渡すべきです。
それでは。
2016年4月10日日曜日
React.js Dev version Warning の使い方
React.js を使うと、Webpack か Browserify と一緒に使うのがオススメです。個人的には Webpack のほうがすきです。時々 Dev バージョンに Warning が表示されたりして、Production にはそういう Warning が表示されません。それは
("production" !== process.env.NODE_ENV) ? Warning : other-process;
に秘密を隠れています。
Webpack を使うと、最後の package には、Node みたいに process.env というコードが追加されます。production バージョンをコンパイルする場合、
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
}
})
],
が使うので、NODE_ENV は "production" になります。それで、Warning とかが実行されないようになります。
実際のコードは Uglify を通すと、false になるところが全部消されるので、production バージョンのコードには残りません。
通常の開発では、活用できるかなと。
それでは、また。
("production" !== process.env.NODE_ENV) ? Warning : other-process;
に秘密を隠れています。
Webpack を使うと、最後の package には、Node みたいに process.env というコードが追加されます。production バージョンをコンパイルする場合、
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
}
})
],
が使うので、NODE_ENV は "production" になります。それで、Warning とかが実行されないようになります。
実際のコードは Uglify を通すと、false になるところが全部消されるので、production バージョンのコードには残りません。
通常の開発では、活用できるかなと。
それでは、また。
React.js のほうがいい、Angular 1.0 と比べたら
つい、来週新機能のリリースが決まりました。同時に、React.js のほうが、Angular JS 1.0 より良いと思われるようになりました。
以前、Framework として、AngularJS 1.0 のほうが上だと思っていましたが、React.js 15.0 のリリースより、DOM が綺麗になったことで、React.js のほうが Better になりました。
まず、data-reactid という属性がなくなりました。次、React.js によりフリーテキストに追加された<span> タグもなくなりました。これは React.js 最後の二つ問題点だと思っていましたから。これで、DOM は前より軽量に、本当の DOM のままになりました。逆に、AngularJS 1.0 の場合、ng-scope など、いろんな要素が追加されて、複雑です。
もう一つ、最大な決め手は React.js のほうが速いです。以前 AngularJS を使うとき、ぐるぐる回る gif の Spinner 止まったり、アニメーションがカクカクしたりすることが結構ありましたが、React.js の場合、一度も見たことがありません。
今作ってるサイトは競合サイトが結構あって、そちらは大体 AngularJS を使っています。同じ Spec のコンピューターを使うと、遅いなと思われました。特に Google Maps API で Marker を地図に入れる処理は UI が止まったようです。それは良くない User Experience です。
さらに、React Native も少し見てみたので、Cordova とか Sencha Touch とかと違って、これは本当の Native App になります。HTML5 Hybrid App 一つの問題は遅いです。React Native を使うと、同じ概念で、本当の App が作れるし、Native のアップだから、多少無駄なコードがあっても、全然問題ないはずです。
重要なことは一つのライブラリを使って、Web、iOS、Android 全部カバーできることです。さらにサーバーサイドレンダリングもできるので、一つのエコシステムになるような気がしています。
これから、時間をみて、今使ってる React.js のバージョンを 15.0.1にして、早くアプグレードしようと思います。
それでは。また。
以前、Framework として、AngularJS 1.0 のほうが上だと思っていましたが、React.js 15.0 のリリースより、DOM が綺麗になったことで、React.js のほうが Better になりました。
まず、data-reactid という属性がなくなりました。次、React.js によりフリーテキストに追加された<span> タグもなくなりました。これは React.js 最後の二つ問題点だと思っていましたから。これで、DOM は前より軽量に、本当の DOM のままになりました。逆に、AngularJS 1.0 の場合、ng-scope など、いろんな要素が追加されて、複雑です。
もう一つ、最大な決め手は React.js のほうが速いです。以前 AngularJS を使うとき、ぐるぐる回る gif の Spinner 止まったり、アニメーションがカクカクしたりすることが結構ありましたが、React.js の場合、一度も見たことがありません。
今作ってるサイトは競合サイトが結構あって、そちらは大体 AngularJS を使っています。同じ Spec のコンピューターを使うと、遅いなと思われました。特に Google Maps API で Marker を地図に入れる処理は UI が止まったようです。それは良くない User Experience です。
さらに、React Native も少し見てみたので、Cordova とか Sencha Touch とかと違って、これは本当の Native App になります。HTML5 Hybrid App 一つの問題は遅いです。React Native を使うと、同じ概念で、本当の App が作れるし、Native のアップだから、多少無駄なコードがあっても、全然問題ないはずです。
重要なことは一つのライブラリを使って、Web、iOS、Android 全部カバーできることです。さらにサーバーサイドレンダリングもできるので、一つのエコシステムになるような気がしています。
これから、時間をみて、今使ってる React.js のバージョンを 15.0.1にして、早くアプグレードしようと思います。
それでは。また。
2016年3月6日日曜日
React.js Modal Component の作り方
いろいろ忙しいです、最近は。プロジェクトのリリースは一ヶ月伸ばされました。ビジネス側はログイン機能がほしいと言ってきたので、五月のプランに入っている Authorisation / Authentication 機能を3月いっぱいで開発することになりました。そのログイン機能はモーダルをポップアップして、ページの真ん中に表示することになっています。
ライブラリを使おうと思ってましたが、調べてみたら、React.js 版はあまり内のプロジェクトにあわないなと思いました。それで、自分で作ろうと思いました。
まず、そのモーダルポップアップは一つになるべきです。つまり一ページの中に一個だけにしたいです。そうすると、children を変えることで、いろんなポップアップが表示できます。次、ページの真ん中に表示するにはまず div とその overlay が必要です。
<div className={ "modal" + this.props.showModal ? " active in" : "" }>
{this.props.children}
</div>
<div className="overlay"></div>
もちろん、overlay は pseudo-element でもできます。
簡単ですね。:)ポップアップするときに、props.showModal を使って、active in class を追加して、画面に表示します。
そのあとは close ボタンなどを作るだけです。
まぁ、もし children にクローズボタンとか入れる場合、React.cloneElement を使ったりして、props を追加すればいいですね。
AngularJS と比較すると、テンプレートベースのモーダルは ng-include を使えば、同じことができます。
ここで、React.js のいいことは、re-render は速いです。すでにレンダリングした Component をもう一度レンダリングする場合、children の type をまず React.js を比較して、もし違うタイプだったら、ツリー全体を更新しますが、もし同じ Component の場合、更新するだけです。DOM 操作は最小限に抑えています。
逆に AngularJS の場合、ng-include はテンプレートの string をチェックしていますので、変わってない場合は Dirty Check でスキップされます。つまり、モーダルが更新されないのです。ここで、ng-include を一回クリアするか、$timeout を使って、リフレッシュしないといけません。または自分でその テンプレートの scope 引数を親から変更したりして、更新します。また Dirty Check が一つ増えます。
React.js の場合、わかりやすいですし、AngularJS 1.0 は面倒くさいなと。AngularJS 2.0 では scope の ダイジェスト対象が設定できるようになるので、速くなると思いますが、やはり、Component という概念は上だと思います。
来週 Polymer を見てみよう。
ライブラリを使おうと思ってましたが、調べてみたら、React.js 版はあまり内のプロジェクトにあわないなと思いました。それで、自分で作ろうと思いました。
まず、そのモーダルポップアップは一つになるべきです。つまり一ページの中に一個だけにしたいです。そうすると、children を変えることで、いろんなポップアップが表示できます。次、ページの真ん中に表示するにはまず div とその overlay が必要です。
<div className={ "modal" + this.props.showModal ? " active in" : "" }>
{this.props.children}
</div>
<div className="overlay"></div>
もちろん、overlay は pseudo-element でもできます。
簡単ですね。:)ポップアップするときに、props.showModal を使って、active in class を追加して、画面に表示します。
そのあとは close ボタンなどを作るだけです。
まぁ、もし children にクローズボタンとか入れる場合、React.cloneElement を使ったりして、props を追加すればいいですね。
AngularJS と比較すると、テンプレートベースのモーダルは ng-include を使えば、同じことができます。
ここで、React.js のいいことは、re-render は速いです。すでにレンダリングした Component をもう一度レンダリングする場合、children の type をまず React.js を比較して、もし違うタイプだったら、ツリー全体を更新しますが、もし同じ Component の場合、更新するだけです。DOM 操作は最小限に抑えています。
逆に AngularJS の場合、ng-include はテンプレートの string をチェックしていますので、変わってない場合は Dirty Check でスキップされます。つまり、モーダルが更新されないのです。ここで、ng-include を一回クリアするか、$timeout を使って、リフレッシュしないといけません。または自分でその テンプレートの scope 引数を親から変更したりして、更新します。また Dirty Check が一つ増えます。
React.js の場合、わかりやすいですし、AngularJS 1.0 は面倒くさいなと。AngularJS 2.0 では scope の ダイジェスト対象が設定できるようになるので、速くなると思いますが、やはり、Component という概念は上だと思います。
来週 Polymer を見てみよう。
2016年2月21日日曜日
React.js もっと速くするコツ
TL; DR:
React.js Component を作る際:
1 shouldComponentUpdate 関数は必ず入れましょう。または pureRenderMixin みたいなモジュールを使いましょう。
2 Component は state を持つであれば、最小限の Component にしましょう。そうすると、setState は最小限 render() 関数を呼び出すし、最小限の DOM 更新します。
3 App に state を置いて、各 Component はできるだけ props を使いましょう。もしいっぱい props があったら、Babel を使って、 {...obj} みたいなシンタックスを使いましょう。
本文:
リリース日が近づいてきました。今作ってる Single Page Web App はもう一丁大きくなって、HTML Node の数は 20k 超えました。AngularJS の場合、もうなんかしないといけないサイズになりますが、React.js はサクサク動くだけです。この辺は DOM 操作とJavaScript 処理の差が出ました。
ただ、React.js を使うだけで、Web App が速くなることではないと思います。やはり前書いたように AngularJS みたいに作り方次第です。AngularJS と同様、React.js Web App を速くする方法は必要なときだけ、データとDOMを更新して、また更新する必要な場所だけ更新します。
AngularJS 1.0 の Dirty Check みたいに、React.js App を更新するには setState() という関数があります。this.setState({newState: newState}) を呼ぶと、その Component の render() 関数が呼ばれて、さらに、Component Tree の比較と行われます。そうすると、React.js は Virtual DOM の変化をキャッチして、実際の DOM を更新します。ただ、Component の render() 関数を呼ぶべきかどうかは shouldComponentUpdate() という関数がコントロールしています。デフォルトでは shouldComponentUpdate() は return true; のみです。この関数は必ず自分で書くべきです。または pureRenderMixin を使って、Component が更新必要なときだけ、true を返して、そうでない場合、false を返すべきです。
さらに、Component は state を持つべきかどうかをよく考えないといけません。一般的に、Web App 全体の App Component に state を置いて、その他の Component は props のみ使うべきです。ただし、carousel や、toggle button みたいな自分の state が必要なとき、state を使いましょう。
例えば、App の構造は:
<div className="app">
<panel1 data={this.state.data1} />
<panel2 data={this.state.data2} />
</div>
App の state に data1, data2 が store から取得して、もし変化がある場合、this.setState({data1: newData}) を呼び出したら、panel1 は更新すべきですが、data2 が変わってないので、 panel2 はこの更新をスキップするために、panel2 component の shouldComponentUpdate 関数は
return this.props.data2 !== nextProps.data2;
を書くべきです。もし data2 は複雑な object や、array の場合、deep check は難しいであれば、更新もそんなに頻繁ではない場合、いつも新しい object を作ってもいいようが気がします。
そうでないと、React.js はいくら速くても、アプリの作りによる重くなることもありうるし、AngularJS より軽いですが、AngularJS みたい遅くなるケースもあります。
React.js Component を作る際:
1 shouldComponentUpdate 関数は必ず入れましょう。または pureRenderMixin みたいなモジュールを使いましょう。
2 Component は state を持つであれば、最小限の Component にしましょう。そうすると、setState は最小限 render() 関数を呼び出すし、最小限の DOM 更新します。
3 App に state を置いて、各 Component はできるだけ props を使いましょう。もしいっぱい props があったら、Babel を使って、 {...obj} みたいなシンタックスを使いましょう。
本文:
リリース日が近づいてきました。今作ってる Single Page Web App はもう一丁大きくなって、HTML Node の数は 20k 超えました。AngularJS の場合、もうなんかしないといけないサイズになりますが、React.js はサクサク動くだけです。この辺は DOM 操作とJavaScript 処理の差が出ました。
ただ、React.js を使うだけで、Web App が速くなることではないと思います。やはり前書いたように AngularJS みたいに作り方次第です。AngularJS と同様、React.js Web App を速くする方法は必要なときだけ、データとDOMを更新して、また更新する必要な場所だけ更新します。
AngularJS 1.0 の Dirty Check みたいに、React.js App を更新するには setState() という関数があります。this.setState({newState: newState}) を呼ぶと、その Component の render() 関数が呼ばれて、さらに、Component Tree の比較と行われます。そうすると、React.js は Virtual DOM の変化をキャッチして、実際の DOM を更新します。ただ、Component の render() 関数を呼ぶべきかどうかは shouldComponentUpdate() という関数がコントロールしています。デフォルトでは shouldComponentUpdate() は return true; のみです。この関数は必ず自分で書くべきです。または pureRenderMixin を使って、Component が更新必要なときだけ、true を返して、そうでない場合、false を返すべきです。
さらに、Component は state を持つべきかどうかをよく考えないといけません。一般的に、Web App 全体の App Component に state を置いて、その他の Component は props のみ使うべきです。ただし、carousel や、toggle button みたいな自分の state が必要なとき、state を使いましょう。
例えば、App の構造は:
<div className="app">
<panel1 data={this.state.data1} />
<panel2 data={this.state.data2} />
</div>
App の state に data1, data2 が store から取得して、もし変化がある場合、this.setState({data1: newData}) を呼び出したら、panel1 は更新すべきですが、data2 が変わってないので、 panel2 はこの更新をスキップするために、panel2 component の shouldComponentUpdate 関数は
return this.props.data2 !== nextProps.data2;
を書くべきです。もし data2 は複雑な object や、array の場合、deep check は難しいであれば、更新もそんなに頻繁ではない場合、いつも新しい object を作ってもいいようが気がします。
そうでないと、React.js はいくら速くても、アプリの作りによる重くなることもありうるし、AngularJS より軽いですが、AngularJS みたい遅くなるケースもあります。
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 を使った方がいいと思われます。
追伸:新しいプロジェクトが始まる場合、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 を使った方がいいと思われます。
登録:
投稿 (Atom)