Introducing Incremental DOM — Google Developers — Medium
Reactやvirtual-dom、Glimmer(Ember)などVirtual DOMの実装は色々あるが、これらのVirtual DOM実装には2つの問題がある
- 既存のテンプレート言語を利用していない(しにくい)
- モバイルでのパフォーマンス、特にメモリに関しては大きすぎる
これらを解決するためにIncremental DOMと言うものを作っている(WIP)
Incremental DOMの特徴としては
- mustache.jsのようにテキストからどういうHTMLになるかがわかるようなデザイン
- 再描画する際のメモリのフットプリントを小さくできる(Virtual DOMは新しいvDOM Treeを作って比較するため、再描画するたびに大きなオブジェクトを作るということになる)
既存のVirtual DOM実装は大きく分けて2つのフェーズを行っている
- レンダリングするための新しいVirtual DOM treeを作る
- 作ったVirtual DOM Treeを前回のVirtual DOM Treeと比較して、実際のDOM Treeへ反映していく
この手法だと、変更点があると保持しておくVirtual DOM Treeが大きくなってメモリにはやさしくない
Incremental DOMはVirtual DOMのモデルを1つのフェーズに変更して行う
- 新しい(Virtual) DOM Treeを作りながら、(実際の) DOM Treeをwalkしつつ変更していく。 (変更がなければ何も作らないし、変更があってもその最小限のVirtul DOM Treeを作ってdiff/patchする)
これを行うために、Incremental DOMでは実際のDOM Nodeに対してメタ情報を埋め込んでいく。 実際のDOM Treeにアクセスするわけだが、これは十分に早い。
Virtual DOM実装は一般的にタグは閉じてるものになってる。
(単純に言えばdocument.createElement()
にあたるAPIしかなく、
Virtual DOMのNodeを作る場合に必ずタグが閉じた状態になる)
一般的なテンプレートエンジンは以下の<section>
のように閉じられてなくても動作する
{template chunk1}
<h1>Hello World</h1>
<section id="body"> /* Content comes after this */
{/template}
これをIncremental DOMでもサポートしたかったので、 OpenとCloseのように組み合わせとなってる。
data.items.forEach(function(item, index) {
elementOpen('x-item', index);
text('item' + index);
elementClose('x-item');
});
つまり、既存のテンプレート言語をそのままIncremental DOM上で再現できるはずという話
詳しい例は
- Virtual DOMより早いの?
- 一般解はまだない。メモリとGCには優しい
- shouldComponentUpdateみたいなメカニズムは?
- 入れたい。メモリ的にちょっとむずかしいかもしれないけど
- Productionで使った?
- No
- 実際に使った?
- Closure Templates
- 他のVirtual DOM実装と連携できる?
- Incremental DOMは零レイヤーな実装
- このうえにVirtual DOM APIをhigh level apiとしておけるかも
- サイズ
- 2.6KB(gzip)
- WIPなのでIncremental DOMへContribution歓迎
@ google/incremental-dom at c4b02e5aac4da34c85d0b8f7eb3df8f44d0987f3
alignWithDOM https://github.com/google/incremental-dom/blob/c4b02e5aac4da34c85d0b8f7eb3df8f44d0987f3/src/alignment.js#L53-53
- DOM Tree Walkerでtraverseしていく
- それぞれNodeを訪ねてメタデータがあるかを見る
- ある: 既存のNode(Virtual DOM的には前回のVirtual DOM)
- そのメタデータからNodeを取り出す
- ない: メタデータをそのNodeから作って保存
- 変更後のNodeがとれたので訪ねたNodeとすり替える(メタデータを入れる)
- 訪問フラグ
メタデータ
- Nodeに対して
__incrementalDOMData
プロパティにメタデータが入ってる - https://github.com/google/incremental-dom/blob/c4b02e5aac4da34c85d0b8f7eb3df8f44d0987f3/src/node_data.js#L99-99
- alignWithDOM()でNodeを取り出す
- メタデータをアップデート
- firstChildをたどって下へ
おおまかな流れ
elementOpenStart('div', '', []);
if (obj.key) {
attr('data-expanded', obj.key);
}
// ここまででargsを貯める
elementOpenEnd();// ここで実際のNodeへ適応
elementClose('div');
- open が enter
- closeが leave
感じでDOM Treeを走査していく流れ
<div>
<p>
</p>
</div>
<span></span>
というDOM Treeだと
openではdiv -> p とたどって、 closeは p -> div -> となりの要素へ(span)
elementOpen('div', null, null,
'style', {
color: 'white',
backgroundColor: 'red'
});
…
elementClose('div');
みたいな要素を組み立てるものを
一時的なバッファとしてargsBuilder
にいれている。
このバッファは、 open->close 次 open->closeとした時に同じ変数を使いまわしてる。 (tag名やattrなどの中身を置き換える)
なので、全部のTreeを走査するときに必要なメモリは 一つのopenの間にある小さなTreeの分だけで良い。
Virtual DOMの場合は、こういう情報を全体のTreeとしてメモリとかに持ってる。