📚

Web Componentのライブラリを作った!

2022/12/18に公開

はじめに

私は元々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上のコードを見ていただければいいのですが、ちょっとだけ説明を入れます。

  1. まずinit()で初期値を定義します。Alpine.jsでいうところのx-dataです。
  2. 実際にstateのオブジェクトを入れる変数は別においており、Object.definePropertyでgetter / setterをおいているためthis.state.countなどとせずにthis.countと記述が可能です。
  3. setterで変更が検知されたらrender()を呼び出して更新をします。

このような仕組みです。

さいごに

Web Componentを使うと結構シンプルに作成ができました。
本フレームワークtmlにはSSR機能などありませんが、ぜひ使ってみて下さい。
(なお現在WIPですので破壊的変更があるかもしれません。あなたのプロジェクトがR.I.P.にならないように本番利用はお避け下さい笑)

Discussion