PetiteVueでTypeScriptを使ってみた

StimulusでもカチッとしたコンポーネントでもないPetiteVue

vuejs/petite-vue: 6kb subset of Vue optimized for progressive enhancement

PetiteVue を見つけたのはこの半年前後くらい。

当時の問題意識は

  • Virtual DOM を採用すると mount した以降の階層がすべて Virtual DOM つまり JS オブジェクトになるのでちゃんと設計しないといけないし、総じて高コスト
  • 生 DOM でそれなりのレールとして使えるものに Stimulus があるが、やや記述が冗長でワンクッション挟まる(すべて attribute の変化がベースになっている)し、まだサンプルも多くないし、あまり直感的に書けない

くらいかな。

CustomElements を試してみて、結局それなりに JS が書けないと使えないし、Shadow DOM は逆にスタイリング周りで考えることが増えるからちょっと違うかもと思ったり、Svelte も試してみて、やはり reactive 自体はよいものだが、こんだけ準備が必要なら Virtual DOM ではないというだけで別に Vue でもたいして変わらないし、Vue を使っているものを置き換えるほどのインセンティブはないと思った。Alpine はディレクティブの中に JS のようなそうでないような謎のコードが増えることをよしとする設計がもう合わないと感じた。

そんな時に PetiteVue を見つけたのだが、すでに Vue を利用している環境(組織)なら Stimulus よりも手軽で Vue っぽい記法が使える PetiteVue はまぁまぁいいんじゃないかと感じた。

特に jQuery を使うのはちょっと憚られるけど Stimulus は実現したいことに対して遠いなと感じるくらいの小さいコンポーネントにはまぁまぁよさそうに見えた。

VueっぽいPetiteVueの書き方

PetiteVue は Alpine の影響を受けてはいるが、ディレクティブごり押しではなく Vue の Options API のようなオブジェクトを scope に与えることもできるので、Vue 2 に慣れている人はだいぶ書きやすいと思う。(逆に Composition API のようには定義できないので注意が必要。)

createApp({
  count: 0,

  increment () {
    this.count++
  }
})

関数にしておいて初期値を与えることもできる。

function Counter (props) {
  return {
    count: props.initialCount,

    increment () {
      this.conut++
    }
  }
}

createApp({
  Counter
})
<div
  id="app"
  v-scope="Counter({ initialCount: 100 })"
>
  {{ count }}
  <button @click="increment">+</button>
</div>

Vueっぽい書き方にTypeScriptを組み合わせる

Vue 2 の Options API のようでいて function にもできるので、定義の function を .ts に分けてあげると、これで type を与えることができる。具体的には以下のような感じ。

interface _Counter {
  count: number
  increment: () => void
}

interface Props {
  initialCount?: number
}

export function Counter (props: Props = {}): _Counter {
  return {
    count: (typeof props.initialCount !== 'undefined')
      ? props.initialCount
      : 0,

    increment () {
      this.count++
    }
  }
}

これで function の中に多少複雑な処理が入っても、event も種類が特定できれば十分に IntelliSense が有効に機能して書きやすい状況に持っていけそう。

いいんじゃなかろうか。

More