Vue.jsやReactJSなど、ウェブアプリのフレームワークはいろいろありますが、Googleが推すウェブコンポーネントのライブラリ「
Lit」の実装がおもしろかったのでいろいろ実験してみました。
ES6の記法で追加された「テンプレートリテラル (テンプレート文字列) - JavaScript | MDN」を使った楽しい実装。
const tag = (strings, ...params) => {
console.log(strings, params);
};
console.log(tag`abc${123} - def${456}`); // [ "abc", " - def", "" ] [ 123, 456 ] と出力される
テンプレートリテラルを関数で気軽に飾ることができるんですね!
Litでは、仮想DOMの代わりにこのテンプレートリテラルを使って差分描画の実装をしているようです。
「reactive data demo」
コンポーネント内でHTMLを書きたいことはあまりないので、データの変化を画面にシームレスに反映するリアクティブの実装だけ採り入れてみることにします。
最終的なコードはこちら。
import { Data } from "./Data.js";
import { BitCheckboxes } from "./bit-checkboxes.js";
import { ReactiveInput } from "./reactive-input.js";
import { sleep } from "https://js.sabae.cc/sleep.js";
onload = async () => {
const d = new Data();
document.body.appendChild(new BitCheckboxes(d));
document.body.appendChild(new ReactiveInput(d));
for (let i = 0; i < 256; i++) {
d.value = i;
await sleep(100);
}
};
肝は、変化を抽出する addListener メソッドを持つ、Dataクラスの実装。
class Data {
constructor() {
const p = new Proxy({}, {
set(target, prop, val) {
target[prop] = val;
if (p.listeners) {
p.listeners.forEach(l => l(target, prop, val));
}
return true;
},
deleteProperty(target, prop) {
delete target[prop];
if (p.listeners) {
p.listeners.forEach(l => l(target, prop, undefined));
}
return true;
},
ownKeys(target) {
return Object.keys(target).filter(name => {
name != "addListener" &&
name != "listeners" &&
name != "toString"
});
},
});
p.addListener = (l) => {
if (!p.listeners) {
p.listeners = [l];
} else {
p.listeners.push(l);
}
};
return p;
}
}
export { Data };
あとは、これに対応した、ウェブコンポーネント bit-checkboxesタグと、reactive-inputタグ。
なかなか便利に使えそうです!