Web Components(+Virtual DOM)ラッパー書いてみた

Concept

『Web Components with Virtual DOM』

Motivation

  • Web Components ラッパーを書いてみたいなーと思った
  • React の JSX がイマイチ気にくわない(JSとHTMLを一緒にするなオジサン)
  • <template> に書いた内容を Virtual DOM 生成器に変換すればいいんじゃね

というような所から人様のライブラリを借りてツギハギして習作してみた次第。借りてきたライブラリ(HTML String パーサと Virtual DOM)は独自実装しても楽しそうなので、やる気があればやる。

もちろん実験品なので、実用には耐えない

Files

  • claylump.polyfill.js(webcomponents.js + window.fetch + es6promise)
  • claylump.runtime.js(runtime template compiler)
  • claylump.js(core library)

Figure


Concept of ahomu/Claylump

Concept


Example

現状のインターフェースは Web Components ネイティブを活かしつつ、テンプレートが mustache インスパイアで、イベント周りは Backbone like events object な感じ。あまり特筆すべきポイントがない。

<template cl-element="x-test">
  <h1 id="clickme">Hello World</h1>
  <h2>{{foo}}</h2>
  <h3>{{baz}}</h3>
</template>

<script>
Claylump('x-test', {
  scope: {
    foo : 'bar',
    baz : 'qux'
  },
  events: {
    'click #clickme': 'onClick'
  },
  attachedCallback: function() {
    console.log('attached!');
  },
  onClick: function() {
    this.scope.foo = 'foo';
    this.scope.baz = 'baz';
    this.invalidate(); // update (diff & patch) DOM!
  }
});
</script>

<template> → Virtual DOM source

冒頭の通り、<template> から Virtual DOM の生成器を生成できればいいんじゃね、ということで...

  1. HTML文字列を <template> から取得する
  2. tautologistics/node-htmlparser でパースしてゴニョゴニョ
  3. テンプレート処理用の評価器を仕込んで、コンパイル済みテンプレートオブジェクトを生成
  4. テンプレートオブジェクトに Object を渡すと Matt-Esch/vdom のVirtual Nodesが生成される
  5. DOM に適用して要素生成

このようなフローにしてみた。3番までのコストが高めだが、これはプリコンパイルで解決できる。<template> の中は謎シリアライズされたオブジェクト文字列になる。もちろん、ランタイムコンパイラを読み込めばブラウザでもコンパイルできる。

1way data-binding

「Data Store + Compiled Template = Virtual DOM」として生成するのを最初のゴールに設定した。

  1. Data Store に対する連続的な変更のたびに invalidate()
  2. invalidate() をタイマーでひとつにまとめてから update() (Virtual DOM の差分検出)
  3. 差分から生成された patch をキューに詰める
  4. requestAnimationFrame のループでキューから patch を取り出して反映する

2way、つまり Real DOM で発生した変更を Data Store に即時反映するのはオプショナルな仕組みとして実装すうr。その場合、基本方針は onChange で引っかける方法を想定したい。

Bundle convenience modules

コア機能以外はすべてモジュール化して、各機能をアドオン的な扱いにすべて分離したい。いま作るとしたらFlux ライクなヘルパーオブジェクトのファクトリモジュールとかもアリかもしれない。オレオレFluxだ!!

  • DOMイベントデリゲーションの仕組み
  • コンポーネント間のメッセージング(考え中)
  • Data Store オブザーバー(考え中)
  • etc..

テンプレート部分は vdom の hook を使ったヘルパーを作ってあるので、それなりの自由度で拡張できるはず。

TODO

必要最低限を実装した段階なので、色々と手の加えようがある。ブラウザ実装の様子見を見ながら試験実装の適用先として育ててみようかと。

  • Component 間のメッセージングはどういう形が良いのか
    • attribute 越しにデータバインディングで共有するのは、ネストしたときに筋が悪い
    • グローバルな空間にあるデータストアを、慎ましく共有するのが手っ取り早いイメージ
  • Data Store と Object Observer の仕組みをどのように結合すべきか
  • 借り物の vdom と html parser を書き換えるべきか

Etc...

その他、開発面のトピック。

ECMAScript 6

AltJSをあまり使わない理由としては、本来的なJavaScriptとの互換性が一番にあったのだけど、昨今のES6トランスパイラーが結構良い感じということみたいなので、sebmck/6to5を試してる。

Modulesも使っているが単純に 6to5 で変換した JavaScript を Browserify にかけて結合しているだけ。多段 Source Map 問題は放置中。

power-assert

乱暴な言い方すると多彩すぎるアサーションメソッド使うのだるい。そして、結果の表示が詳細なの良いなぁ〜という理由で twada/power-assert を導入してみた。

CI

ahomu/Phalanx では TravisCI から Drone.io に乗り換えたりしてたけど、今なら何が良いんでしょうね。昨今よく聞くので試してないのは CircleCI か。

ということで

最近フロント系から浮気してばっかりだったので、原点回帰して JavaScript を書きたい今日この頃 ( `^´)=3