picodomは、「1 KB Virtual DOM builder and patch function.」と名乗るような、ごく小さな仮想DOMフレームワークです。コンパクトで取り回しが良さそうなのですが、実際に使おうとすると何点か気になることがありました。
picodom自体のAPI
picodomには、APIがたった2つしかありません。
picodom.h
h()
関数が、仮想DOMを生成する関数です。引数は、h(タグ名, オプション, 中身1, 中身2…)
のようになっていて、オプション
には{class: 'hoge', href: '/foo'}
のように、タグの属性と値をオブジェクトで指定します。そして、複数指定できる中身
は、
- 他のVDOMオブジェクト
- 文字列や数値(テキストノードになります)
- 上2つの配列
などを、いくつでも指定できます。
picodom.patch
patch()
で、仮想DOMを実際のDOMに差分適用させます。引数は、patch(親のDOMノード, 中身のDOMノード, 古いVDOM, 新しいVDOM)
となっていて、返り値は新しく生成した中身のDOMノードです。
気になった点
コア機能としては、たしかにこの2つがあればVDOMを実現できます。ただ、やはり最小化優先のため、そのままの使い勝手はよくありません。
連続した差分更新
通常、VDOMの差分更新を行う場合、「古いVDOM」は「直前に更新したVDOM」以外を指定することがないので、それをハンドリングするような関数を作っておいたほうがいいでしょう。
// PicodomのVDOMに含まれる、
// string、number、function、Array、Object、null、undefined、booleanだけが対象
const deepCloneVDOM = (arg) => {
// 即値、function(コピー不要)、null
if(typeof arg !== 'object' || arg === null) return arg;
// 配列
if(Array.isArray(arg)) {
return arg.map(x => deepCloneVDOM(x))
}
// Object
const ret = {}
for(let key in arg) {
ret[key] = deepCloneVDOM(arg[key]);
}
return ret
}
let oldVDOM = null;
let innerNode = null;
let parentNode;
function update(newVDOM){
innerNode = picodom.patch(parentNode, innerNode, oldVDOM, newVDOM);
oldVDOM = deepCloneVDOM(newVDOM);
}
なお、反映済みのVDOMを破壊的に変更してしまっても差分を正しく取れるようにするため、保存時にディープコピーを行うようにしてあります。
タグ生成
JSXをつないでもいいのですが、そこまで大げさにするのであればpicodomの値打ちがなくなる気もします。ということで、h
の引数のオプションを省略できるようにしたり、その他工夫を考えてみました。
// dataを省略できるようにしたVDOM生成関数
const tag = (name, data, ...children) => {
// dataがVDOM or その配列 or 文字列の場合
if(typeof data !== 'object' || (data.tag && data.data) || Array.isArray(data)) {
children.unshift(data);
data = {};
}
return picodom.h(name, data, children);
};
const TAGS = ['a', 'abbr', /* 中略 */ 'wbr'];
const specificTag = (name) => (...rest) => tag(name, ...rest);
TAGS.forEach((tagName) => {
tag[tagName] = specificTag(tagName);
});
このような補助関数を用意すれば、
const{h1, p, span, div, a} = tag;
let someVDOM = div({class: 'foo'},
h1('title'),
p('文章中の', a({href: 'http://qiita.com/'}, 'リンク'));
);
のように、VDOMもそこそこきれいに組み立てられるようになります。
モジュール化
むろんこれもpicodomの守備範囲ではないのですが、複数のモジュールに分けたぐらいの開発をするとなれば、別にレイヤーを組み立てる必要がもちろん出てきます。
まとめ
picodomは、そのまま使うというより、自分なりのVDOMフレームワークを作るベースとして使う、というのが適当なのかもしれません。