Edited at

React.jsがVirtualDOMを採用していて嬉しい事

More than 3 years have passed since last update.

今回はReact.jsのVirtualDOMについて簡単に触れておきたいと思います。

VirtualDOMについては別途AdventCalendarがあるので詳しくはそちらを見てください。自分もReact.jsのVirtualDOMの実装について書いています。

http://qiita.com/advent-calendar/2014/virtual-dom

本当は↑だけでいいのですが、このAdventCalendarのどこかで触れておきたかったので重複する部分も多いですが簡単に触れておきます。


VirtualDOMの嬉しい点

JavaScriptを使ってDOMを操作して画面の表示を切り替えていくようなアプリケーションの場合、ユーザー体験を損ねないためにも更新されるDOMは最低限にしたくなります。

例えばBackbone.jsを使っていると、基本的にはView単位でrenderするのでViewを細かく分割していくことが必要になります。そうなるとViewが増えて複雑さが増してきて辛くなってきます。

(jQueryで直接DOMを触ってとかも出来ますが....)

Angular.jsの場合は、Dirty checkingをして変更があれば再描画という形なので監視する対象が増えていくるとパフォーマンスが落ちる問題があります。

(v2になるとObject.observe使うようになって変わると思いますが)

React.jsの場合は、setState(forceUpdate)が呼ばれるとそのComponent以下がrerenderの対象となります。

これだけだと当然毎回広範囲のDOMが更新されてツラい感じになるのですが、React.jsではVirtualDOMとしてメモリ上にDOMの状態を保持していてその前後の状態を比較し差分の部分だけを実際のDOMに反映してくれます。


  • ちなみにCSSのstyleなんかもオブジェクト形式で指定することで該当のstyleだけを更新してくれます。

var Hoge = React.createClass({

getInitialState() {
return {
style: {
color: "#ccc",
width: 200,
height: 100
}
};
},
onChange() {
var style = _.clone(this.state.style);
style.color = "#ddd";
this.setState({ style: style});
},
render() {
return (
<div style={this.state.style} onClick={this.onChange}>xxx</div>
);
}
}

それによって、パフォーマンス的に優れているというのはもちろんですが、パフォーマンスがそこまで重要視されないようなアプリケーションでもTopレベルの要素にアプリケーションの状態を持たせておいてそれをsetStateでどんどん更新するという雑なアーキテクチャも可能になります。

サーバーサイドでのレンダリングみたいですね。

つまり、DOM周りの面倒でパフォーマンスに影響を与える部分をReact.jsに任せることでアプリケーションの実装を単純に出来るという特徴があります。

アプリケーション開発者がVirtualDOMについて意識する場面は、昨日の記事で書いたkey属性の指定と、パフォーマンス向上を目的にshouldComponentUpdateを実装する場面があります。


shouldComponentUpdate

shouldComponentUpdateについてはComponentのLifecycleの記事でも書きましたが、実装されていない場合は常にrerenderするようになっていて、これを実装してfalseにすることでこのComponentを含むそれ以下のComponentがrerenderされなくなります。

    var shouldUpdate =

this._pendingForceUpdate ||
!inst.shouldComponentUpdate ||
inst.shouldComponentUpdate(nextProps, nextState, nextContext);

https://github.com/facebook/react/blob/master/src/core/ReactCompositeComponent.js#L684-L687

最低限のDOMしか更新されないなら常にrerenderしても問題ないのではと思うと思いますが、それだと対象のComponent以下の全てのComponentのVirtualDOMのtreeを作って比較し、差分があるかどうか計算する必要があるので、実際のDOMに触らないとはいえコストがかかります。

対して、対象のComponentのStateとPropの前後の状態を比較し差分がある場合だけ、対象のComponentを含むそれ以下のComponentのVirtualDOMのtreeを作って比較してrerenderした方がコストが低いことがわかるかと思います。


React.js以外では

React.js以外にもVirtualDOMを実装しているライブラリとしては、mercuryMithrilなど色々アリますし、Ember.jsも2.0でVirutalDOMの実装を検討しているようです。

https://github.com/emberjs/rfcs/pull/15

また、実装について知りたい人はvdomやdekuのソースから読むといいかもしれません。

https://github.com/Matt-Esch/vdom

https://github.com/segmentio/deku


というわけで今回はVirualDOMについて簡単に触れておきました。

明日は、Componentの拡張について取り上げたいと思います。