Edited at

WebAssemblyで実装された仮想DOM asm-dom をさわってみた

More than 1 year has passed since last update.


asm-domとは

https://github.com/mbasso/asm-dom

asm-domはWebAssemblyで実装された仮想DOMライブラリです。

仮想DOMのdiff、patch部分のアルゴリズムをWebAssemblyに任せたミニマルな実装なので、Reactのような多彩な機能はないようです。WebAssemblyなので、爆速を期待してしまいます。

最初のコミットが2017/2/26なのでまだ一年も経っていないライブラリです。

ライセンスの表記snabbdomの名前があるので何か関係があるのかもしれません。


Simon Friis Vindum, 2015 as part of project snabbdom



使い方

C++でも書けるようなのですが、今回はwebらしくjsで使ってみました。

npm i --save asm-dom

npm i -D arraybuffer-loader

webpackでバンドルするので、webpackとwebpack-dev-serverも入れます。

npm i -D webpack webpack-dev-server

webpack.config.jsはこんな感じ。.wasmファイルを読み込むためにarraybuffer-loaderを足しています。

var resolve = require('path').resolve;

module.exports = env => {
const dist = env.prod ? 'docs' : 'dist';
return {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: resolve(__dirname, dist),
pathinfo: !env.prod,
},
node: {
fs: 'empty'
},
devtool: env.prod ? 'source-map' : 'eval',
devServer: {
contentBase: resolve(__dirname, dist),
historyApiFallback: true,
compress: true,
port: 9000
},
module: {
loaders: [
{
test: /\.wasm$/,
loaders: ['arraybuffer-loader']
}
]
}
};
}

JSでの使い方の軽いドキュメントがあるので、それに沿った形です。


virtual-domとの速度を比べてみた

仮想DOMライブラリであるvirtual-domとasm-domの速度を比べてみました。

30000個のdivのテキストをランダムな数値で入れ替えて時間を吐き出してみました。

仕様ブラウザは Chromeでバージョン61 です。


圧倒的にasm-domの方が速かった

その差は約2倍でした。ここまで差が出るとは思いませんでした。さすがWebAssembly。virtual-domがそもそも遅いのかと疑ってしまいたくなります。Reactとかとも純粋なdiff, patchの処理だけで速度を比べてみたいです。

asm-domの計測結果

asm-dom.png

virtual-domの計測結果

virtual-dom.png


計測コード

コードはgithubのレポジトリに置いておきました。

DOMの定義の仕方はhyperscript風なので、virtual-domと同じようにh()でノードを作ってpatch()で変更を適応します。


asm-dom

import init from 'asm-dom';

init().then(asmDom => {
const { h, patch } = asmDom;

const root = document.getElementById('root');

let vnode = h('div', {}, []);
patch(root, vnode);

let cnt = 0;
const id = setInterval(() => {
console.time('apply patch in 30000 elements');
const list = [];
for (let i = 0; i < 30000; i++) {
list.push(
h('div', {}, [ Math.random() + '' ])
);
}
const newVnode = h('div', {}, list);
patch(vnode, newVnode);
vnode = newVnode;
console.timeEnd('apply patch in 30000 elements');
cnt++;
if (cnt >= 10) {
clearInterval(id);
}
}, 100);
});



virtual-dom

const h = require('virtual-dom/h');

const diff = require('virtual-dom/diff');
const patch = require('virtual-dom/patch');
const createElement = require('virtual-dom/create-element');

let cnt = 0;
let tree = h('div', {}, []);
let rootNode = createElement(tree);
document.body.appendChild(rootNode);

const id = setInterval(function () {
console.time('apply patch in 30000 elements');
const list = [];
for (let i = 0; i < 30000; i++) {
list.push(
h('div', {}, [ Math.random() + '' ])
);
}
const newTree = h('div', {}, list);
const patches = diff(tree, newTree);
rootNode = patch(rootNode, patches);
tree = newTree;
console.timeEnd('apply patch in 30000 elements');
cnt++;
if (cnt >= 10) {
clearInterval(id);
}
}, 1000);


Web Componentsも試してみました。

WebAssemblyで実装された仮想DOM asm-dom でWeb Componentsを使ってみた