◆ JSX の代わりに htm を使う
◆ Vue2 の頃とはレンダー関数周りも違ってて bind する関数の変更が必要

久々に Vue に触れましたが やっぱり Vue のテンプレートは嫌です
SFC 使うなら ビルド必要になるし JSX でいいかなと思いましたが ビルド無しで HTML ファイル単体でも使いたいです

htm

Vue2 の頃も htm を使っていたので今回も使おうとしたのですが Vue3 でレンダー関数が色々変わってました
基本的には良い変更です
props の特殊なものが減って変換せずそのまま渡すのでも十分です
また h 関数がグローバルなものになり import したものを使えます
以前はレンダー関数の引数として受け取っていたので レンダー関数内で毎回 htm に bind する必要がありました
bind 処理自体は重たくないはずですが コンポーネントごとに書くのが面倒でした
その点 グローバルであれば html 関数を作ってそれを使い回せます

逆に children は関数にしないと警告が出ました
以前は出てなかったような気がします
警告なので無視でもいいのですが 関数スロットにしたほうがパフォーマンス的に好ましいそうです

const html = htm.bind(
(type, props, ...children) =>
h(type, props, children)
)



const html = htm.bind(
(type, props, ...children) =>
h(type, props, typeof type === "string" ? children : () => children)
)

にしました
関数にするのはコンポーネントのときだけのようです
div など普通のタグのときまでコンポーネントにすると中身が表示されなくなりました
コンポーネントの場合は type がコンポーネントのオブジェクトになってるので type で判定してます

とりあえず使ってみたサンプルです

<!doctype html>
<meta charset="utf-8" />

<script type="module">
import { createApp, ref, h } from "https://unpkg.com/vue@3.3.7/dist/vue.esm-browser.js"
import htm from "https://cdn.jsdelivr.net/npm/htm@3.1.1/mini/index.module.js"
const html = htm.bind(
(type, props, ...children) =>
h(type, props, typeof type === "string" ? children : () => children)
)

const Component = {
props: ["foo"],
setup(props, { slots }) {
return () => html`
<div>
<div>foo: ${props.foo}</div>
<div>slot: ${slots.default()}</div>
</div>
`
},
}

const App = {
components: {
Component,
},
setup() {
const count = ref(1000)
const text = ref("")

return () => html`
<div>
<button onClick=${() => count.value++}>${count.value}</button>
${[1, 2, 3].map(n => {
return html`
<div>
<span>${n}</span>
<input
value=${text.value}
onInput=${(event) => text.value = event.target.value}
/>
</div>
`
})}
<${Component} foo=${count.value}>${text.value}</>
</div>
`
},
}

createApp({
components: {
App,
},
setup() {
return () => html`<${App} />`
},
}).mount("#root")
</script>

<div id="root"></div>

各 ref で .value を書かないといけないのが面倒ですが 省略できるところと省略できないところがあって一貫性がなくわかりづらかったので こっちのほうが好きです
またリスナの設定にも関数を書かないといけないですが ここも関数なのか中身の処理なのか曖昧で読みづらいのが Vue のテンプレートの嫌いなところだったのでこっちのほうがいいです