📚
Web Componentのライブラリを作った!
はじめに
私は元々tmlというhtmのバインディングを作っていたのですが、stateとかを実装するためにWeb Componentを取り入れてみました。
実際のコード
<x-counter />
<script type="module">
import { $, TMLElement } from 'https://cdn.jsdelivr.net/gh/kstdx/tml@0.4/dist/tml.min.js'
class Counter extends TMLElement {
init() {
return { count: 0 }
}
render() {
return $`
<>
<button :click=${() => this.count++}>increment</button>
<p>${this.count}</p>
</>
`
}
}
customElements.define('x-counter', Counter)
</script>
これが動きます。(関数コンポーネントは未実装です)
実際にJSFiddleで公開しています
コードを読み解いてみる
まず
import { $, TMLElement } from '{url}'
でライブラリを読み込みます。
$
はTagged Template Literal用の関数です。HTMLみたいな文字列をオブジェクトに変換するものです。
TMLElement
はstateなどのコンポーネントの機能を実装しているクラスです。
次に
class Counter extends TMLElement {
のところではCounter
という、TMLElement
を継承したクラスを定義しています。
また、
init() {
return { count: 0 }
}
の場所でstateを定義しています。この場合count
というstateを初期値0
で定義しています。この場合this.count
でアクセスできます。
render() {
return $`
<>
<button :click=${() => this.count++}>increment</button>
<p>${this.count}</p>
</>
`
}
の場所では実際のコンポーネントの中身を定義しています。
stateが変更されるたびに呼び出されます。
stateの仕組み
そこまで複雑ではないので詳しくはGitHub上のコードを見ていただければいいのですが、ちょっとだけ説明を入れます。
- まず
init()
で初期値を定義します。Alpine.jsでいうところのx-data
です。 - 実際にstateのオブジェクトを入れる変数は別においており、
Object.defineProperty
でgetter / setterをおいているためthis.state.count
などとせずにthis.count
と記述が可能です。 - setterで変更が検知されたら
render()
を呼び出して更新をします。
このような仕組みです。
さいごに
Web Componentを使うと結構シンプルに作成ができました。
本フレームワークtmlにはSSR機能などありませんが、ぜひ使ってみて下さい。
(なお現在WIPですので破壊的変更があるかもしれません。あなたのプロジェクトがR.I.P.にならないように本番利用はお避け下さい笑)
Discussion