はじめに
他の vDOMベースFW との違い
Virtual DOM (vDOM) とは?
直訳すると、仮想 DOM、となりますが、DOM とは実は直接、
関係がありません。
なので、勘違いしてしまいそうな名前ですが、DOM を置き換えるもの
では ありません。
端的に表現すると、HTML のサーバーサイド・レンダリング(SSR)を
クライアントサイドにもってきて、HTML の再構築前後の差分のみ
抽出し DOM API を用いてDOM に適用するようにした仕組み、と
なります。
仮想DOM APIを用いて、仮想DOMツリー(HTML片)を組み立て、その仮想
DOMツリーが更新された場合、更新前後の仮想DOMツリーを比較、差分を
抽出し、DOM API を用いて、DOM に差分(変更)を反映します。
仮想DOM は、DOMを補完する技術です。
仮想DOM では、DOM と同じように HTML の構造をツリー構造として
表現しています。
DOM の更新タイミング
vDOM ベース・フレームワークの多く?では、任意のタイミングで?
用意された差分抽出・適用の関数を呼び出して、DOM への差分適用を
行うことがor行うこともできますが、Vue.jsでは DOM の更新は
フレームワークにより管理され自動でバッチ更新されるようになって
います、手動で行うことはできません。
vDOM ベース・フレームワークでは、vDOMツリー差分のみの DOM への
適用により、ブラウザでのUIレンダリングが走る頻度を少なくしています。
DOMのみの Vue.js 1.x までは、 DOM 処理のまとめてバッチ適用に
より、ブラウザでの UI レンダリングの走る頻度を少なくして
いました、vDOMベースとなった Vue.js 2.x以降は、他のvDOMベース
のフレームワークと同様に差分のみの DOM への適用により、
ブラウザでのUIレンダリングが走る頻度を少なくしています。
Vue.js (や React)では、フレームワークがvDOMツリーの構築(旧vDOM
ツリー)→ステートの更新に伴うvDOMの更新(新vDOMツリー)→旧vDOMツリー
と新vDOMツリーの差分の抽出→DOMへの反映の処理をライブラリがカプセル化
し隠蔽してくれているので、ユーザーが意識する必要は(基本的には)ありません。
パフォーマンス上etcの理由から、ユーザー(利用開発者)が差分が DOM
に反映されるタイミングを意識しておくor ユーザーが差分が DOM に
反映されるタイミングを(間接的に)コントロールすることも場合に
よっては必要になります。
Vue.jsも(Reactも)画面の再描画の(差分がDOMに反映される)タイミング
をユーザーがある程度(間接的に)コントロールできるようになっています。
vDOM ベース・フレームワークが有効なケースとは?
HTMLがリアルタイムで変化し、UIレンダリングの発生頻度が
問題になるアプリ、で、主にリアルタイムで画面が変化する
ゲームアプリ etcで、威力を発揮する気がします。
と、以前書きましたが、現在は、ブラウザのDOM→UIレンダリング
まわりの改良により、vDOMを利用することで発生する新旧vDOM
ツリー間の差分計算→DOMへの反映がパフォーマンス上のネックに
なるようになっていて、リアルタイムで画面が変化するゲームアプリ
etcの開発には、vDOMベース・フレームワークは向いていません。
UIをコンポーナントとして分割して開発し、UIレンダリングの性能が
重視されないアプリに向いています。
Vue.js の vDOM
Vue.jsは、元々、DOMベースのフレームワークであり、vDOMは
採用されていませんでした。
Vue.js については「vDOM に対する対応を行う予定はない」と
作者であるEvan You 氏が、以前、Issue にて明言し、
「vDOM対応すると、別物になってしまう」とも書かれていました。
ので、DOM APIと同じレベルのレイヤーで DOM APIとの親和性の高い
vDOM(API)ライブラリが出てくれば対応が行われるかもしれません、
と書いていました。
軽量な低レベルvDOM(API)実装 snabbdom がリリースされたことで
状況が変化し、snabbdom の採用により内部的には vDOM を採用
しつつも、利用者は vDOM を意識しなくても利用できる形で実装された
Vue.js2.0 の開発が進められ、リリースされました。
Vue.js は 2.0.0 以降、2.xでは、snabbdom をフォークして
カスタマイズした vDOM(API)実装を内蔵しています。v3.0.0以降、
フル・スクラッチで?書き直した vDOM実装を内蔵しているようです。
vDOMについては、現状の高レベル・レイヤでの(特定のフレーム
ワークとタイトに結合した)実装ではなく、より低レベル・レイヤ
での実装が出てきて、フレームワーク内部で共通に利用されるように
でもならない限り、一般化 してはいかないような気がしますと、
以前、書いていました。
パフォーマンス上の問題 etcにより、vDOM(API)実装は汎用化
され、フレームワーク内部で共通利用される方向には進みません
でした。
Vue.js の DOM → DOM + vDOM へのスイッチ時点では、
DOMのみの実装 より DOM + vDOM 併用の実装のほうが
クライアントサイドにおいて性能面で有利でした。
Vue.js は、DOMベース → DOM+vDOM の移行を行った後、
バージョンアップを経ながら、内部実装を洗練してきています。
vDOMありきで設計されたフレームワークではないことが
他のvDOMベース・フレームワークとの大きな違いです。
vDOMベース・フレームワークの課題?
vDOMでは、vDOMツリー(HTML(片))の構築をDOMとは
わけて行い、vDOMツリーの更新前後の差分の抽出を行う
ので、差分まわりのアルゴリズム (特に差分をDOMに反映
するタイミングetcを制御するアルゴリズム)が優秀でない
とダメな気がします。
差分まわりのアルゴリズムは改良されてきていますが、JSで
実装されているので、パフォーマンス面では頭打ちになり
つつある感じです。
Rust etcでvDOMまわり(の一部)を実装、WebAssembly
にコンパイル、JSから呼び出す方向に向かう可能性も
あります。
JS と WebAssembly 間のやりとりには、パフォーマンス上
のオーバーヘッドが存在するので、vDOMまわりを WebAssembly
での実装に置き換えたとしても、パフォーマンスが向上するとは
限りません。
パフォーマンス上のネックかつWebAssembly化によりパフォーマンスが
向上する箇所がWebAssembly化されていくのかもしれません。
サーバーサイドでは、vDOMまわり(の一部)を Rust etcで
Node.js、Deno、Bun で利用可能なネイティブモジュールと
して実装し、JSで呼び出す方向も考えられますが、クライアント
サイドとサーバーサイドで異なる実装となるので、この方向に
進むことはないと考えられます、クライアントサイドを棄てるなら
別ですが。
クライアントサイドで、vDOMベース(WebAssembly化も含めて?)
よりDOMベースのほうがパフォーマンス面で有利なら、クライアント
サイドではvDOMを棄ててDOMベースの実装に回帰していく可能性が
高いように思います。
サーバーサイドについても、SvelteやSolidJSはSSRではHTML
テンプレート(JS/TS含む)を(HTML/CSSを生成する)効率的な
文字列付加コードにコンパイルする造りになっているので、
vDOMベースの実装よりvDOMを利用しない実装のほうが
パフォーマンス上、明確に優位で後方互換性の維持が可能なら、
vDOMベースFWの多くはvDOMを棄てる方向に向かう可能性が高い
でしょう。
vDOM自体、ブラウザのUIレンダリングまわりの実装に
パフォーマンス上の問題があったことで産まれた技術
なので、パフォーマンス上の優位性がなくなれば、廃れ、
消えていくでしょう。
vDOM ベース・フレームワークのこれから
vDOMは、ブラウザのUIレンダリングまわりの実装が熟れておらず、
DOM APIを呼び出すと頻繁にUIの再描画が行われることにより
発生していたパフォーマンス上の問題を、UIの再描画と切り離して、
HTML(片)の更新を行い、変更のあったHTML片(HTMLの更新差分)
のみをDOM に反映することにより、UIの再描画の頻度を減らす
ことで回避し、ブラウザでのUIのパフォーマンスを引き上げて
いました。
近年、ブラウザのUIレンダリングまわりの実装の改善により、
DOM(API)呼び出し時のUIの再描画のパフォーマンスが改善されて
きたため、ネイティブで実装されている DOM ではなく
JavaScript で実装された vDOM を利用する根拠となっていた
パフォーマンス上の優位性は なくなっています。
Vue.js / React より後発のフレームワークである Svelte /
SolidJS では、クライアントサイドではvDOMを利用しない
DOMのみの実装で、vDOMを利用したフレームワークよりも
上の性能を(ほぼ)実現しています。
Svelte / SolidJS は、CSR/SSRの双方に対応し、サーバーサイド
では、HTMLテンプレート(JS/TS含む)を(HTML/CSSを生成する)
効率的な文字列付加コードにコンパイルする造りになっています。
Svelte / SolidJS は、これらをベースとするフルスタックな?
フレームワーク SvelteKit / SolidStart(開発中) が存在し、周辺の
ライブラリも整備中であるため、Vue.js / React にとって代わる
かは未知数です。
vDOM ベース・フレームワーク の多くは、近視眼的には、ハイブリッド
な(vDOM がパフォーマンスで有利なケースでは vDOM を利用、DOM が
パフォーマンスで有利なケースでは DOM のみ利用する) or 脱vDOMな
(DOMのみ利用する)アーキテクチャに移行していく、と考えられます。
React は、DOM のみの実装 から vDOM との併用 への移行
が行われた Vue.js とは異なり、最初から vDOM ベースであり、
vDOM ありきの vDOM と密結合した構造となっている(はずな)
ので、ハイブリッドな or 脱vDOMなアーキテクチャに移行する
には、アーキテクチャの 再設計が必要 となる(はずな)ので、
難しく移行しない可能性があります、移行するにしても
時間がかかる のではないか、と思われます。
React が ハイブリッドな or 脱vDOMなアーキテクチャに
移行するケースでは、ユーザーにとっての破壊的な変更を伴う
可能性があります。
サーバーサイドでは、クライアント・サイドとは異なり、DOMよりvDOM
のほうが軽量であり、パフォーマンス上のネックとなりえる新旧vDOM間
の差分計算やvDOM→DOMへの反映の処理が不要であるため、また、
(サーバーサイドでは)パフォーマンス上、DOMよりvDOMのほうが有利で
あるため、vDOMベースフレームワークでは、サーバーサイドで
コンポーネントの組み立てを行い、クライアントサイドでは描画のみを行う、
サーバー・コンポーネントを仕組みとして、最近、導入しています。
サーバー・コンポーネント、Vue.js(本体)では、サポートされて
いませんが、Vue.jsベースのフレームワークである Nuxt3 では
サポートされています。
Reactでは、RSC(React Server Component) として仕様策定され、
React本体 と Reactベースのフレームワークでサポートされつつ
あります(Next.jsではサポート済み、Remixでは未サポート)。
クライアントサイドとサーバーサイドは、WEBアプリにおいて、車の両輪
です。
vDOMにパフォーマンス上のネックのないサーバーサイドに逃げるのでは
なく、クライアントサイドでの性能向上に取り組んでいかないと、vDOM
ベースFWの多くは、今後、消えていく、かもしれません。
Vue.js
vDOMを利用しない、(Vue.jsランタイム利用、クライアントサイドではDOM
ベースの)JavaScriptコードを生成する、テンプレート・コンパイラの
SFC(Single File Component)用新モード、Vapor Mode が、年内の
ベータ版?リリースに向けて、開発中です。
Vapor Mode は、CSR/SSR の双方に対応し、vDOMを利用しない
コードを生成します。
Vapor Mode では、Vue.jsランタイムの利用範囲が縮小され、
より少ないメモリを使用する、パフォーマンス重視のJavaScript
コードが出力されます。
Vapor Component (Vapor Mode利用)と
Vapor-less Component(Vapor Mode非利用)は
混在可能となる予定です。
Vapor Mode は、<script setup> + Composition API で
記述されたコンポーネント(SFC)のみ、サポートします。
vDOM を利用している(内部の)コードも、ブラウザの
UIレンダリングまわりetcの実装の改善に伴い、今後も
変更されていくことが予想されます。
Vue.js は、パフォーマンスでの優位性から、DOM のみ利用から
vDOM との併用にシフトしているので、vDOM の利点がなくなれば、
vDOM との併用から vDOMを利用しない実装(クライアントサイド
ではDOM利用)にシフトしていくと思われます。
Vapor Mode は、短期的には、オプショナルな機能として
リリースされます。数年?掛けて完成度を高めながら、中〜長期的
には現行のvDOMベースの実装を置き換えていく方向に向かう、と
思われます。
Vapor Modeは 旧来のOptions APIをサポートしないため、
Vapor Modeが主流になったとしても、現行のvDOMベースの実装は
Options APIのサポートのために当分はオプショナルな機能として
残り続けるはずです。
Vue.jsでは、vDOM と DOM の違いは、内部で吸収されている、
ので、ユーザーが意識する必要はありません。
vDOMベースフレームワークの欠点
コンポーネントの再利用に問題があります。
同一フレームワーク内で再利用する場合は問題ないのですが、
異なるフレームワーク間でコンポーネントを再利用しようと
すると、色々と問題があります。
vDOMベースのフレームワークの場合、vDOM(API)実装を含む
ランタイム まわり一式をコンポーネントと共にロードする
必要があります。
vDOMベースの別フレームワークの場合、含まれているvDOM(API)
実装は、共通の部品ではなく、それぞれの固有の実装となっている
ので、 vDOMベースのフレームワークからvDOMベースの別フレームワーク
のコンポーネントを再利用しようとすると、vDOM(API)実装を含む
ランタイム全体がダブります。
異なるフレームワーク間で再利用できるコンポーネントの開発を
行う場合、vDOMベースのフレームワークは採用対象外となり、
Svelte etc の DOMベースフレームワークが 採用されること
が多いです。
UIコンポーネント・ライブラリでは、Vue.js / React 用 が
それぞれ開発されていたり、どちらかにしか対応していなかったり
します。
Vue.js 用 と React 用 のどちらも提供されている場合、
DOMベースフレームワークで開発したコンポーネントをラッピングして
Vue.js / React 用 のコンポーネントにしていたり します。
vDOMベースのフレームワークの存在は、コンポーネントの自由な再利用
の観点では 障害にしか なっていません。
推敲中...