core-vapor のディレクトリ構成
リポジトリの用語について
これから行う説明について,vuejs/core と vuejs/core-vapor のどちらもに適応される話は v3 のリポジトリ
として表記します.(例,v3 のリポジトリでは,~~~)
何が core-vapor 固有の話で,何がもと (vuejs/core) からある話なのかを区別することで差分を予想しながら core-vapor の理解に繋げます.
主要なパッケージ
v3 のリポジトリは pnpm workspace によってモノレポで管理されています.
各パッケージは /packages
ディレクトリに配置されています.
https://github.com/vuejs/core-vapor/packages
そして,それらのパッケージは大きく分けてコンパイラとランタイムの 2 つに分けられます.compiler-
で始まるパッケージはコンパイラに関連するパッケージで,runtime-
で始まるパッケージはランタイムに関連するパッケージです.
- https://github.com/vuejs/core-vaporpackages/compiler-core
- https://github.com/vuejs/core-vapor/packages/compiler-dom
- https://github.com/vuejs/core-vapor/packages/compiler-sfc
- https://github.com/vuejs/core-vapor/packages/runtime-core
- https://github.com/vuejs/core-vapor/packages/runtime-dom
core-vapor では新たに compiler-vapor
と runtime-vapor
が追加されています.
- https://github.com/vuejs/core-vapor/packages/compiler-vapor
- https://github.com/vuejs/core-vapor/packages/runtime-vapor
また,次に重要なパッケージが reactivity
です.ref
や computed
, watch
などの実装はランタイムパッケージからは独立して @vue/reactivity
として提供されています.
こちらは /packages/reactivity
に配置されています.
そして,Vue.js のエントリとなるパッケージは /packages/vue
に配置されています.core-vapor
においては,これに加え,/packages/vue-vapor
と言う Vapor Mode のエントリとなるパッケージが追加されています.
- https://github.com/vuejs/core-vapor/packages/vue
- https://github.com/vuejs/core-vapor/packages/vue-vapor
全体像:
compiler-core
compiler-core
はその名の通りコンパイラのコア部分を提供します.
コンパイラのパッケージはこれらの他に,compiler-dom
と compiler-sfc
などがありますが,
core は,sfc や dom といった特定の用途や特定の環境に依存しないコアな実装です.
Vue.js にはさまざまなコンパイラが存在しています.
例えば,template
オプションを利用するとランタイム上でテンプレートがコンパイルされます.
createApp({
template: `<div>{{ msg }}</div>`,
setup() {
const msg = ref("Hello, Vue!");
return { msg };
},
}).mount("#app");
しかし,このテンプレートは見てわかる通り,SFC でも同様のテンプレート構文を利用しています.
<script setup lang="ts">
import { ref } from "vue";
const msg = ref("Hello, Vue!");
</script>
<template>
<div>{{ msg }}</div>
</template>
また,これ以外にも HTML の innerHtml として記載したものをコンパイルするケースなど,Vue.js のテンプレートとしてのコンパイルは様々です.
このような様々な用途の共通部分を提供するのが compiler-core
だという理解で概ね問題ありません.
具体的には,template
を render
関数にコンパイルするコアな実装が含まれます.
compiler-dom
Vue.js では,DOM の関する操作やコード生成を行うものは 環境依存である という考えのもと,これらはコアから分離されています.
これは後ほど runtime の方でも登場します.
コンパイラに関して言えば,DOM イベントに関するコードを生成したり,特定の DOM 要素に関するコードを生成したりする実装が含まれます.
Vue.js のイベント修飾子あたりを想像してもらうとわかりやすいかもしれません.
例えば,@submit.prevent
といった修飾子は,
(e: Event) => e.preventDefault()
のようなコードが必要となり,これは DOM API に依存するコード生成です. このようなものを提供するのが compiler-dom です.
例:
compiler-sfc
これは名前の通り SFC (Single File Component) に関するコンパイラです.
具体的には,<script setup>
や <style scoped>
などの機能を提供します.
多くの場合,このコンパイラは別パッケージになているバンドラ等のツールのプラグインに呼ばれることで機能します.
有名な例としては,Vite で利用される vite-plugin-vue や,webpack で利用される vue-loader などがあります.
runtime-core
ランタイムのコア部分を提供します.
こちらも DOM には依存しない,コンポーネントのランタイムの実装や,仮想 DOM とそのパッチ,スケジューラの実装などが含まれます.
パッチ処理 (renderer) に関しては,実際に DOM 操作が行われそうな雰囲気がありますが,runtime-core では非 DOM API 依存に定義された interface の呼び出しのみを行っており,
実際の関数は runtime-dom に実装され,注入されています.(依存性逆転の法則を利用しています.)
interface:
createRenderer という関数が option として実際のオペレーションを受け取る(runtime-core では直接呼び出さない):
runtime-dom
上記で説明したうちの,実際の DOM オペレーションの実装や,それらを core に注入する実装が含まれます.
他にも,compiler の説明でも触れた,実際に DOM イベントを処理するための実装なども含まれています.
(compiler-dom はこれらの呼び出しを行うコードを出力するための実装です.)
reactivity
名前の通り,Vue.js のリアクティビティシステムを提供します.
どこかで,「Vue.js のリアクティビティシステムは out of box で利用可能だ」,という話を聞いたことがあるかもしれませんが,これはこのパッケージが他のパッケージに依存せず独立して実装されているためです.
そして,この「独立している」という点も Vapor Mode の実装においては重要なポイントとなります.
それもそのはず,少しネタバレをしておくと,Vapor Mode は仮想 DOM を使わずにリアクティビティシステムを活用することで画面を更新していくわけですが,実際のところこのリアクティビティのパッケージにはほとんど変更が入っていません.
詰まるところ,Vapor の機能の一部としてスッと使えてしまうほど Vue.js のランタイムには依存していないのです.
compiler-vapor, runtime-vapor
さて,ようやく今回のメインです. 名前の通り,Vapor Mode のコンパイラとランタイムの実装です.
Vapor Mode は現在 R&D のフェーズであるため,なるべく upstream にある既存の実装には手を加えずに済むように独立したパッケージとして実装されています.
そのため,既存の runtime, compiler と大きく被る部分もありますが,実はこの部分関しても重複して実装しています.
このパッケージでどのような実装がされているかなどはこれから見ていく (というかそれがこの本のメインの話) なので,ここでは省略します.
ざっくり,パッケージの全体構成がわかったところで早速 Vapor Mode の実装を理解するために必要なソースコードを読んでいきましょう!