はじめに
他の 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 への差分適用を行うことはできません。
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ベースのフレームワークであるNuxt(v3以降)ではサポートされています。
Reactでは、RSC(React Server Component) として仕様策定され、React本体とReactベースのフレームワークでサポートされつつあります(Next.jsではサポート済み、React Router v7 では未サポート)。
クライアントサイドとサーバーサイドは、WEBアプリにおいて、車の両輪です。
vDOMにパフォーマンス上のネックのないサーバーサイドに逃げるのではなく、クライアントサイドでの性能向上に取り組んでいかないと、vDOMベースFWの多くは、今後、消えていく、かもしれません。
Vue.js
vDOMを利用しない、(Vue.jsランタイム利用、クライアントサイドではDOMベースの)JavaScriptコードを生成する、テンプレート・コンパイラのSFC(Single File Component)用新モード、Vapor Mode が、v3.6での実験的機能?としてのリリースに向けて、開発中です。
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 は、短期的には、オプショナルな機能としてリリースされます。数年?掛けて完成度を高めながら、中〜長期的には<script setup>のSFCでは現行のvDOMベースの実装を置き換えていく方向に向かう、と考えられます。
Vapor Modeは 旧来のOptions API & <script setup>のSFC以外のコンポーネントをサポートしないため、Vapor Modeが主流になったとしても、現行のvDOMベースの実装はOptions API & <script setup>のSFC以外のコンポーネントのサポートのため(当分は)残り続けるはずです。
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ベースのフレームワークの存在は、コンポーネントの自由な再利用の観点では障害にしか なっていません。
推敲中...