はじめに
他の 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(旧Remix v2)では未サポート)。
クライアントサイドとサーバーサイドは、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ベースのフレームワークの存在は、コンポーネントの自由な
再利用の観点では障害にしか なっていません。
推敲中...