Vapor ModeをつまみぐいしてVue 3.6に備えよう

Programming

この記事は さいが崖 Advent Calendar 2025 17日目 の記事です。

この記事は Vue 3.6 alpha-6 時点の仕様を基に書いています。Vue 3.6 正式版では内容が変更になる可能性があるため、ドキュメントを確認することを強く推奨します。

Vue 3.6 なんかすごそう

こんにちは。ここのえです。

Vueの次期バージョンである 3.6 のalpha版が、今年7月12日にリリースされました。Vueを使っていない人にとっては「あ、そう……」という感想しか出てこないと思います(それはそう)。マイナーバージョンの更新ですが、今回採用される Vapor Modealien-signals によってVueの開発体験が大きく変わります。簡単に言うと、めちゃくちゃ早くなります。

GitHub - vuejs/vue-vapor: Vue Vapor is a variant of Vue that offers rendering without the Virtual DOM.
Vue Vapor is a variant of Vue that offers rendering without the Virtual DOM. - vuejs/vue-vapor
GitHub - stackblitz/alien-signals: 👾 The lightest signal library
👾 The lightest signal library. Contribute to stackblitz/alien-signals development by creating an account on GitHub.

本記事ではVue 3.6の主要機能であるこの二点に絞って、解説とサンプルコード・現alphaバージョンでの注意点について説明していきます。

Vapor Modeとは?

Vapor Mode(蒸気モード1)は、Vueのシングルファイルコンポーネント (.vue)のための新しいコンパイルモードで、バンドルサイズの削減・パフォーマンスの向上を目的としています。

今までのVueでは仮想DOMを用いてDOMの変更を行っていましたが、Vapor Modeでは仮想DOMを廃止し、solid.js と同様にDOMを直接操作します。ただしコンポーネントの実装においては、これまでと変わらない .vue ファイルで、 refcomputed がいつも通り使えます。

Vapor Modeはコンパイルモードの一種であるため、オプトイン機能として提供されます。アプリケーション全体に適用する必要はなく、コンポーネント単位で有効・無効化でき、既存のSFCと混在して使用することができます。そのため既存のライブラリと共存することもでき、段階的に移行できる点もgoodです。

注意点としては、Composition API形式のコンポーネントしか対応していません。まさか2025年の新規実装で今更Options APIでVueを書いていることはないと思いますが、Vue 2.x時代から既存のプロジェクトをアップグレードしている場合は注意が必要です。いい機会なので、そろそろComposition APIへの移行はどうでしょうか……?(小声)

Vapor Modeの使い方(1) 仮想DOMと一緒にハイブリッドに使う

通常の仮想DOMと混在させて使う場合は、 main.tsvaporInteropPlugin を読み込みます。3.6が出てしばらくの間はライブラリの対応待ちになるので、基本的にこの使い方がメインになると思います。

main.ts
import { createApp, vaporInteropPlugin } from 'vue'
import App from './App.vue'

createApp(App)
  .use(vaporInteropPlugin) // プラグインとして読み込む
  .mount('#app')

Vapor Modeを使うコンポーネントは、<script setup vapor> とします。

VaporTest.vue
<script setup vapor lang="ts">
import { ref } from 'vue'

const count = ref(0)
</script>

<template>
  <div>
    <h2>Vapor Counter</h2>
    <p>これはVapor Modeのコンポーネントです</p>
    <button @click="count++">count is: {{ count }}</button>
  </div>
</template>

呼び出し元のコードは普通にコンポーネントを呼び出せばOKです。

App.vue
<script setup lang="ts">
import VaporTest from '@/VaporTest.vue'
</script>

<template>
  <h1>Vapor Test App</h1>
  <p>ここは普通のVDOMです</p>
  <VaporTest />
</template>

<style scoped></style>

無事表示されました。

Vapor Modeの使い方(2) 全部Vapor Modeにする

「俺は常に最新を走ってるからよ……」という漢気プログラミングをするならアプリケーション全体をVapor Modeにすることもできます。main.tscreateVaporApp() します。

main.ts
import { createVaporApp } from 'vue'
import App from './App.vue'

createVaporApp(App).mount('#app')

最初に読み込む App.vue から、読み込むコンポーネントはすべて <script setup vapor> でなければいけません。

App.vue
<script setup vapor lang="ts">
import VaporTest from '@/VaporTest.vue'
</script>

<template>
  <h1>Vapor Test App</h1>
  <p>App.vueもvaporにする必要があります</p>
  <VaporTest />
</template>

<style scoped></style>

3.6 alpha-6 時点では、createVaporApp() でアプリケーション全体をVapor Modeにすると、<template><style> だけのHTML要素だけのコンポーネントもエラーになります。スクリプトを書く必要がないコンポーネントでも、例外なく全てに <script setup vapor> を記述する必要があります。

で、実際速くなるの?

速くなるかならないか、それが一番気になるところでしょう。js-framework-benchmark の結果を見てみましょう。vue-vaporを軸にしてcompareすると以下の通りです。

比較対象はv.3.5.13のvaporです。これは2024年11月リリースなので、恐らくVapor Modeの実装はvuejs/core-vapor版であり、現バージョンと異なる可能性があります。ご参考程度にお願いします。

仮想DOMを使った今までのVueと比べても、明らかに速度が出ています。solid, svelteにはやや負けているものの、そこまで差は離れていないので許容できるレベルの速度差といったところでしょうか。ただclear rowsについてはvue-vaporが恐ろしく速く、solidにも大差をつけて勝っています。

ではメモリのアロケーションと、転送サイズについてはどうでしょうか?

仮想DOMに割いていたメモリが削減されたおかげで、消費量が抑えられています。全体的に見てもsolid, svelteとほぼ同水準まで下がっています。転送サイズについてはさすがにsolidには遠く及びませんが、compressed sizeはそこそこ健闘してるといったところでしょうか。早くなったとはいえ、first paintがまだ遅いですね。この辺りは3.6正式版のベンチマークで改善がみられるか期待したいですね。

余談:Vueは遅い印象でしたが、ベンチを見る限り現バージョンでもReactより早いらしい……。いつの間にこんな早くなったの……?

alien-signals

ちがいます。

こっちです。

GitHub - stackblitz/alien-signals: 👾 The lightest signal library
👾 The lightest signal library. Contribute to stackblitz/alien-signals development by creating an account on GitHub.

alien-signals はVue 3.6より導入されるリアクティビティシステムです。こちらはVapor Modeとは関係なく、独立した別機能なのでアップデートするだけで恩恵が受けられます

開発者のJohnson氏はVue 3.4のリアクティビティシステム最適化に関わっていた方で、Push-Pull方式のsignalアルゴリズムを採用しています。コアになるアルゴリズムにおいて「Array/Set/Mapを使用しない」、「関数の再帰を禁止する」といった制約で徹底した最適化を行うことで、最大限のパフォーマンスを引き出しているそうです。

signalはリアクティブシステムとしてSolid・Angular・Preact・Qwikなどで採用されています。Vueのドキュメントに各フレームワークの比較があります。
https://ja.vuejs.org/guide/extras/reactivity-in-depth#connection-to-signals

Svelte 5のRunesもsignalベースのアルゴリズムのようです。
https://svelte.jp/blog/runes

Johnson氏は3.5でPreactに似たPullベースに移行されたことがきっかけで、独立したプロジェクトとして最適化の研究としてalien-signalsの開発を行っていたそうです2

肝心の速度ですが、上記のalien-signalsのリポジトリにあるVue 3.4との比較を見てもわかる通り、圧倒的に速くなっています。加えて、リアクティビティシステムのベンチマークであるJS Reactivity Benchmarkの結果ではSolidやPreact Signals、Svelte 5を抜いて最速です。現verのVueと比べると250msぐらい速いです。そんなに早いのかコレ。

GitHub - transitive-bullshit/js-reactivity-benchmark: Benchmark comparing different standalone JS reactivity / signals frameworks.
Benchmark comparing different standalone JS reactivity / signals frameworks. - transitive-bullshit/js-reactivity-benchma...

こちらはVue 3.6にするだけで速度差を実感できると思うので、ぜひ既存のプロジェクトをアップグレードして試してみてください。

まとめ

今回はVue 3.6の主要機能であるVapor Modeとalien-signalsについて紹介しました。Vapor Modeは昨年、Misskey開発者のしゅいろさんの記事で知ったのですが、3.6の記事を書こうとしたら正式に採用されており「ついに来たか……」という感じです。全体的なトレンドを見ても、メモリ削減・パフォーマンス向上のためのVDOM脱却の流れなのかなあとは思います。

Vue 3.6のVapor Mode・alien-signalsの詳細については、こちらが非常に分かりやすく、これまでのVueの歴史についても学べるのでオススメです。正直これを読めば、私の記事は全く読まなくてもいいかもしれませんが……

Vue 3.6は特にパフォーマンスの向上に力が入ってるなあという印象です。もしかしたら、「Vueは遅いよね」という定説はもはや過去のものになりつつあるのかもしれません……。というかそうあってほしい。世の中全部とは言わないけど50%ぐらいVueのプロジェクトになってほしい。

参考

  1. vaporwave の “vapor” と同じ、”蒸気”の意です ↩︎
  2. https://github.com/stackblitz/alien-signals?tab=readme-ov-file#background ↩︎

コメント

タイトルとURLをコピーしました