フレームワーク「Svelte」「Solid」が話題。画期的だった仮想DOMと脱仮想DOMへの流れ

2024年4月8日

執筆

山内 直

有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)に所属するテクニカルライター。出版社を経てフリーランスとして独立。ライター、エディター、デベロッパー、講師業に従事。屋号は「たまデジ。」。著書に『Bootstrap 5 フロントエンド開発の教科書』、『作って学べるHTML+JavaScriptの基本』など。

 

監修

山田 祥寛

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。
主な著書に「独習」シリーズ「これからはじめるReact実践入門」「改訂3版 JavaScript本格入門」他、著書多数

「フロントエンド技術の今」の連載。第3回のテーマは「仮想DOM」です。

DOMとは?

今回のテーマである「仮想DOM」は、字面を捉えるとDOMを仮想化したものですが、その目的はDOM更新の高速化です。仮想化の意義を語る前に、まずはDOMについて押さえておきます。

▲DOMとはHTMLドキュメントをJavaScriptオブジェクト化したもの

DOMはHTMLの構造をオブジェクト化したもの

DOMは、「Document Object Model」の略で、シンプルに表現すると

HTMLドキュメントをJavaScriptのオブジェクトにしたもの

DOMを利用することで、JavaScriptからはHTMLのタグ個々がオブジェクトに見えるので、参照や変更が容易になります。DOMの世界では、階層化されたオブジェクトのツリーとしてドキュメントを表現します。

DOMから画面を描画する

DOMでは、描画に備えてレンダーツリーを構築します。レンダーツリーとは、描画に必要なノードのみで構成される木構造のデータです。イメージはDOMに似ているのですが、<head>や不可視のノード(display: none;を指定されたノードなど)はレンダーツリーからは除外されます。これにより、レンダーツリーに基づいて描画すれば良いという基礎ができあがります。

DOMができあがる、もしくは変更されると、レンダーツリーの構築と描画が実行されるということです。これが効率的に実施されるかということはUXに直結する部分であるので、ブラウザエンジンの実装における要所と言えます。それについては「オブジェクトモデルの構築」などを一読することをお勧めします。

仮想DOMはどこが優れていたのか?

DOMは非常に優れた仕組みですが、その構築や変更には、小さくない処理コストがかかります。もちろん、影響範囲が広がれば、処理コストも比例して大きくもなります。特に、上位のノードの変更は下位のノードにも影響を与えるので、できれば影響範囲を最小にとどめたいものです。そこで、不要なDOMの変更を最小限とするために、DOMと更新コードの間の抽象化レイヤーとして考案されたのが「仮想DOM」(Virtual DOM)です。ここからは、仮想DOMに対する実際のDOMを「リアルDOM」として区別します。

▲仮想DOMではDiffとPatchで高速化する

Reactで導入されて人気となった仮想DOM

Reactのコンポーネント指向(連載第1回を参照)とは、コンポーネントに見た目とロジックを持たせて一連の描画を任せるものです。ユーザの操作に応じて間違いなく効率的にそのDOMを変更するのは、開発者サイドにとって大きな負担です。しかし、コンポーネントの世界では、開発者がDOMの存在を意識することはなく、コンポーネントという「部品」のみを意識すればよいわけです。

もちろん、コンポーネントもDOMを隠蔽しているだけで、内部的にはDOMを差し替えることで、最終的な結果を描画しています。ブラウザは、DOMの変更を検知したら、レンダリングツリーを構築し、画面に反映させます。

一見なんということもない処理ですが、実際に変更された部分は少しだけで、コンポーネントの大部分は変更されていないとしたら、このような更新処理は無駄が多すぎます。更新処理による画面のちらつきは、UXを低下させます。

そこでReactのようなフレームワークでは、コンポーネントのレンダリングに際して、仮想DOMを挟むことで、リアルDOMの変更を最小限にしています。では、どのようにリアルDOMの変更を最小限にするのでしょうか?

仮想DOMでは差分計算(diff)とパッチ処理(patch)で高速化する

仮想DOMでは変更されたときに、変更前後の違いを計算するためにdiff(different(差異)の意)という処理を実行しています。diffのアルゴリズムの詳細は割愛しますが、単純に先頭(ルートノード)から比較するというものではなく、ピンポイントでの差異を検出するといった優れたものです。Unixのdiffコマンド、VSCodeのdiff画面のように「真に異なる」箇所のみをピックアップします。

そして、diffの結果を受けて、本当に必要な更新のみをリアルDOMに対して実施するのです。これをパッチ(Patch)といいます。これによって、リアルDOMが影響を受ける部分が限定され、結果としてブラウザ画面の再描画も抑えられるというわけです。

仮想DOMは万能ではない

仮想DOMは優れた仕組みで、画期的でした。Reactで採用されて、コンポーネントによる抽象化によるオーバヘッドを限りなく抑えてくれる効果があるので、一気に知名度が上がりました。ただこの仮想DOMも、万能ではありません。それは、仮想DOMの構築にもコスト(計算、メモリ)が必要で、それはリアルDOMに比べると軽いものとはいえ、ゼロではないということです。また、差分計算にも当然のごとく計算コストがかかります。抽象化された事象を具象化する際には、何事も大きなコストが必要となるものです。それは仮想DOMにおいても例外ではありません。

脱仮想DOMを果たした人気のフレームワーク「Svelte」「Solid」

仮想DOMは優れた仕組みですが、ここにきて「脱」仮想DOMとも捉えられるような動きが出てきました。State of JSという調査で、「関心のある(interest)」JavaScriptフレームワークとして「Svelte」(https://svelte.dev/)と「Solid」(https://www.solidjs.com/)という見慣れない名前が、この数年でトップにランクされるようになったのです。
参考:「state of javascript 2022」

ReactやVue、Angularは「利用されている(Usage)」フレームワークとしては依然として上位を独占していますが、関心では「Svelte」「Solid」の後塵を拝するという状況になっています。明らかに興味関心が上昇中であるこれらのフレームワークは、どのようなものなのでしょうか?

▲ピンポイントで変更するコードを生成して高速化する

SvelteとSolidはコンパイル後のJSを生成する

両者に共通するのは、コンポーネントの記述方法こそ異なりますが、事前コンパイルを行い、ピンポイントでDOMを変更するコードを生成することで高速化を図っていることです。アプリのコードが実行時に解釈されるのではなく、ビルド時に解釈されて変換されるということです。これにより、Reactなどで実施される、抽象化された要素を実行時に解釈する必要はなく、最終的なDOMの更新といったプリミティブなコードのみでアプリを実行できます。当然のことながら、仮想DOMは使いません。コードの読み込み時間も、初期レンダリングに要する時間も短縮でき、UXの向上につながります。

Svelteは独自ファイルと構文を持つフレームワーク

Svelteは、Reactive.jsから派生したフレームワークです。2016年に登場した新しいフレームワークですが、機能強化が急激に進むことで人気を伸ばし、上記のようにReactやVueを抑えるほどの人気となりました。

Svelteでは、コンポーネントをJSXではなくピュアHTML、CSS、JavaScript/TypeScriptの構文で記述します(イベントなどはon:~といった独自構文となる)。また、各コンポーネントは拡張子が.svelteであるファイルに分けて記述していきます。そのため、Reactを使ってきた開発者には学習コストが必要になってきますが、コンポーネントの状態はローカル変数で宣言すれば良いなど、シンプルな記述でアプリを開発できます。

SolidはJSXでコンポーネントを記述できるフレームワーク

Solid(SolidJS)は、Reactに大きく影響を受けたコンポーネント指向のフレームワークです。2021年にバージョン1.0がリリースされた、極めて新しいフレームワークですから、登場とともに人気が急上昇したということになります。

Solidでは、コンポーネントの記述にJSXを使用します。Reactによく似た形式でコンポーネントを記述できるので、Reactの開発者がSolidを理解しやすい、始めやすいという利点があります。Solidでは、コンポーネントの状態は「プリミティブ」という値(ゲッタ)とセッタの組み合わせで管理し、これにより参照と変更がコンパイル時に追跡しやすくなるというメリットが生まれます。

Solidにおけるコンパイル後のコードは、あらゆるフレームワークを押さえてVanilla JSに次ぐ処理速度を達成したということですから(公式サイトより)、より高速な動作を期待する場合には有効な選択肢でしょう。

仮想DOMはもはや不要なのか

仮想DOMを使わずに高速化を達成したフレームワークが人気となると、「脱仮想DOMの流れはますます加速し、これから使われなくなっていくのでは?」という疑問が湧くかもしれません。しかしながら、仮想DOMにも脱仮想DOMにも、それぞれ長所と短所があり、そういったものは完全に置き換わらずに住み分けていくものです。仮想DOMの代表のReactと脱仮想DOMのSvelteとを例に取れば、以下の表のような違いがあります。

▲SvelteとReactの比較

これを見ると、Svelteは仮想DOMを使わないことによる高速な動作と小さなバンドルサイズ、JSXを使わないコンポーネント記述に利点がありますが、エコシステムがまだまだ未発達であることから、過去の資産を考慮すればReactから乗り換えてしまおうという動きにはなかなかなりにくいとも読めます。しかしながら、SolidのようにJSX構文でReactの延長で開発できるフレームワークも登場しているので、一部を置き換えていく感じで脱仮想DOMへの動きは進むのではないでしょうか。

おわりに

今回は、脱仮想DOMの流れを、DOMの基本からReactなどによる仮想DOMについて触れながら紹介しました。この記事をきっかけに、改めてDOMの基本に触れることや、新進のフレームワークに関心を持ってくれる読者がいれば幸いです。

関連記事

人気記事

  • コピーしました

RSS
RSS