Open4

お手軽! DOMライブラリ制作記録

ihasqihasq

Litのタグ付きテンプレートリテラルとReactの関数型コンポーネントを組み合わせた、ノンディレクティブでステートフルなDOMマニピュレータのテンプレートライブラリを書いてみる

完成予想図

import { html, $, on } from "@mylibrary"

function App() {

    const count = $(0);

    return html`
        <h1>${count}</h1>
        <button ${{ [on.click]: () => $[count]++ }}>
            Increment
        </button>
    `
}

write(document.body, App());

ぱっと見はReactだがここにJSXやHooksは登場せず、関数そのものは初回レンダリング時のみ実行され、値の更新は後に紹介する「ポインタ」が担う
テンプレート関数であるhtmlの構造は(...x) => xといったシンプルなものであり、入力を解析する仕組みは持たない

このライブラリの最大の特長はSymbolを拡張した特殊な「ポインタ」オブジェクトを用いて、宣言的かつ高効率なデータフローを実現している点である

const count = $(0);
$[count] // 0
typeof count // "object"

$[count.toString()] // 0

関数$toString()でsymbolを返却する特殊なオブジェクトを提供する
そのため、countポインタにリスナの取り付けやSymbol.disposeプロパティの付与を行える

ihasqihasq

valueプロパティにポインタを埋め込むだけで、簡単に双方向データバインディングを実装できる
双方向データバインディングには配列を使用する
値は常に0番地に代入される

function Input() {

    const input = $("")

    return html`
        <h1>${input}</h1>
        <input ${{ [at.type]: "text", value: input }} />
    `
}

配列の1番地には代入時の副作用を記述できる
第1引数にはStrixInputEvent が、unsafeでは生の HTMLInputEvent が充てられる

ihasqihasq

要素のイベントには子要素の値の変更で発火される StrixEffectEvent という独自実装が存在する
イベントリスナ属性では @effectで使用できる

const OnEffectElement = () => {
    let count = 0;
    return () => html`
        <label @effect=${({ oldValue }) => console.log(`changed from ${oldValue}`)}>
            ${count}
        </label>
    `
}
ihasqihasq

useState 的な振る舞いも可能

const LikeUseState = ({ $ }) => {

    const count = $(0); // 動的な値

    return () => html`
        <h1>${$[count]}</h1>
        <button @click=${() => $[count]++}>
            Increment
        </button>
    `
}

$ は組み込みのポインタ関数であり、独立した実装は以下のリポジトリにある