petite-vue 最速 使い方
2021年7月3日(日本時間)、Vueの創始者Evan Youが新たなパッケージをリリースしました。
petite-vue
petite-vue(プティットゥ・ヴュー「プチVue」の意)は先進的な機能を備えた、超軽量なVueの下位互換です。スタンダードなVueと同等のテンプレート構文・リアクティビティモデルを有していますが、かつてサーバーフレームワークで所々に書かれていたような、ちょっとしたインタラクションを置き換えられるよう特化しています。
- 5.7KB以下
- DOMベース(仮想DOMを使用しない)
- @vue/reactivityで動作
使い方
ビルドは不要で、CDNから読み出すだけで使えます。
<script src="https://unpkg.com/petite-vue" defer init></script>
<!-- anywhere on the page -->
<div v-scope="{ count: 0 }">
{{ count }}
<button @click="count++">inc</button>
</div>
- petite-vueで制御したい範囲を
v-scope
でくくる。 - HTMLがパースされた後に実行したいスクリプトには
defer
属性をつける。 -
v-scope
を持っている要素すべてを初期化するにはinit
属性を使う。
より実際的には createApp
を使います。
<script type="module">
import { createApp } from 'https://unpkg.com/petite-vue?module'
createApp({
// exposed to all expressions
count: 0,
// getters
get plusOne() {
return this.count + 1
},
// methods
increment() {
this.count++
}
}).mount()
</script>
<!-- v-scope value can be omitted -->
<div v-scope>
<p>{{ count }}</p>
<p>{{ plusOne }}</p>
<button @click="increment">increment</button>
</div>
petite-vue の適用範囲を絞るためにマウントのターゲットを指定することもできます。
createApp().mount('#only-this-div')
複数指定もできます。
createApp({
// root scope for app one
}).mount('#app1')
createApp({
// root scope for app two
}).mount('#app2')
要素ごとに mounted
や unmounted
のライフサイクルフックが使えます。
<div
v-if="show"
@mounted="console.log('mounted on: ', $el)"
@unmounted="console.log('unmounted: ', $el)"
></div>
要素をリアクティブ化するには v-effect
を使用します。
<div v-scope="{ count: 0 }">
<div v-effect="$el.textContent = count"></div>
<button @click="count++">++</button>
</div>
コンポーネント
petite-vueでは、再利用可能なコンポーネントは関数として記述します。
<script type="module">
import { createApp } from 'https://unpkg.com/petite-vue?module'
function Counter(props) {
return {
count: props.initialCount,
inc() {
this.count++
},
mounted() {
console.log(`I'm mounted!`)
}
}
}
createApp({
Counter
}).mount()
</script>
<div v-scope="Counter({ initialCount: 1 })" @mounted="mounted">
<p>{{ count }}</p>
<button @click="inc">increment</button>
</div>
<div v-scope="Counter({ initialCount: 2 })">
<p>{{ count }}</p>
<button @click="inc">increment</button>
</div>
あるテンプレートの値を再利用したいときは、$template
キーを使用します。
<script type="module">
import { createApp } from 'https://unpkg.com/petite-vue?module'
function Counter(props) {
return {
$template: '#counter-template',
count: props.initialCount,
inc() {
this.count++
}
}
}
createApp({
Counter
}).mount()
</script>
<template id="counter-template">
My count is {{ count }}
<button @click="inc">++</button>
</template>
<!-- reuse it -->
<div v-scope="Counter({ initialCount: 1 })"></div>
<div v-scope="Counter({ initialCount: 2 })"></div>
reactive
メソッドを使ってグローバルな状態を有するストアも作れます。
<script type="module">
import { createApp, reactive } from 'https://unpkg.com/petite-vue?module'
const store = reactive({
count: 0,
inc() {
this.count++
}
})
// manipulate it here
store.inc()
createApp({
// share it with app scopes
store
}).mount()
</script>
<div v-scope="{ localCount: 0 }">
<p>Global {{ store.count }}</p>
<button @click="store.inc">increment</button>
<p>Local {{ localCount }}</p>
<button @click="localCount++">increment</button>
</div>
使える機能、使えない機能
petite-vueのみ
v-scope
v-effect
-
@mounted
および@unmounted
Vueとふるまいが異なる
$el
createApp()
- コンポーネント
- カスタムディレクティブ
Vueと同じ
-
{{ }}
テキストバインディング -
v-bind
(:
を含む) -
v-on
(@
を含む) v-model
-
v-if
/v-else
/v-else-if
v-for
v-show
v-html
v-text
v-pre
v-once
v-cloak
reactive()
nextTick()
petite-vueでは使えない
-
ref()
,computed()
など - テンプレートrefs(セレクタを使ってね)
- render関数(仮想DOMは非サポート)
- コレクション型 (Map, Setなど)へのリアクティビティ
- Transition, KeepAlive, Teleport, Suspense
- ディープな
v-for
v-on="object"
-
v-is
&<component :is="xxx">
-
v-bind:style
の自動修正
デモ
CodeSandboxに入れてみましたが、初期描画時にうまく動きませんでした。
デモブラウザの更新ボタンを押すと正しく表示されます。
考察
以下、petite-vueが作られた背景として考えられそうなものを挙げてみます。
Reactとの差別化
Vueと並べてべて名前が挙がることの多いReactは、近年そのSSR/SSGフレームワークであるNextJSと合わせてWebフロントエンド開発の覇権を握りつつあります。Reactは仮想DOMという仕組みを採用し、それによって高いパフォーマンスを出していますが、反面その動作速度はある程度のところで頭打ちになってしまうという欠点を持っています。Vueは実は本質的には仮想DOMに基づいておらず、特定の機能にしか使われていないので、そうした機能を削ぎ落とすことで軽量化と高速化を図り、Reactとの差別化路線を打ち出したようにも見えます。
Svelteとの競争
冒頭に書いたように、petite-vueはサーバーフレームワーク(Ruby on RailsとかDjango、Laravelあたりでしょうか)で書かれたHTMLにちょっとしたインタラクションをJSで加えるみたいな用途に最適と謳っており、Evan You自身もそうしたフレームワークであるAlpineに言及したうえで、それよりも軽量であると主張しています。
しかし軽量さで近年注目を集めたといえばSvelteです。その創始者のRich Harris(Rollup.jsの作者でもあります)は軽量さと記述量の少なさを追い求めており、特に軽量さに関してはTwitter上でEvan Youに直接メンションしてアピールするなど対抗心を燃やしていました。Evan Youもそれに触発されたのか、Rollup.jsよりも高速なバンドラであるViteを開発し、実際これはSvelteにもVueにも採用されています。こうした経緯から、「Vueを軽量化する」というモチベーションがEvan Youの中にあったものと思われます。
Hotwireの存在
そしてこのような用途、つまり「HTMLにJSを部分的に用いる」というアプローチを前面に打ち出しているのがHotwireです。HotwireはBasecampが開発しているライブラリで、既存のWebアプリケーションにこれを振りかけるとSPAライクになりユーザー体験が向上するというアイデアに基づいています(ここの理解はわりとあいまい)。一からWebアプリケーションをリプレイスするのはやはり大変であり、既存のサービスをいかに新しいものに置き換えていくかというアプローチには小さく始められることが求められます。petite-vueはそうした需要を満たすために、Vueの下位互換として改めて作られたようにも思いました。
Discussion