仮想DOMってすげーんだぜ!


この記事は福岡若手Sier_bc Advent Calendar 2019の11日目の記事です。

はじめに

今回は仮想DOMについて書いてみました。

  • フロントエンドに興味のある方
  • 仮想DOMについて知りたい方

を対象にしています。

そもそもDOMって何よ?

「Document Object Model」の略ですが、Wikipedia先生の説明では

DOMは、HTML文書やXML文書(あるいはより単純なマークアップされた文章など)をオブジェクトの木構造モデルで表現することで、ドキュメントをプログラムから操作・利用することを可能にする仕組みである。Documentの種類、操作に用いるプログラミング言語の種類に依存しない仕様である。

要するに、
HTMLを構築する木構造データのことだよ!プログラミング言語で操作できるよ!
ってことです。ここで注意しなければならないのは、MDNの説明にも

(前略)ふつうは JavaScript を使用しますが、 HTML、 SVG、 XML などの文書をオブジェクトとしてモデリングすることは JavaScript 言語の一部ではありません。

とあるように、
DOMはJavascriptを用いて操作することができるけど、Javascriptの一部じゃない
ってことです。さらに、WebブラウザはDOMからHTMLを解析してWebページをレンダリングします。

本記事では仮想DOMと区別して、通常のDOMをリアルDOMと呼称します。

じゃあ仮想DOMって何よ

正体はJavascriptのオブジェクトです。
JavascriptのオブジェクトでリアルDOMを仮想的に作って、

  1. 仮想DOMを二つ用意
  2. 一方の仮想DOMをJavascriptで操作(一般的にリアルDOMを操作するより速い)
  3. 変更前後の仮想DOMの差分を比較
  4. 差分だけをリアルDOMに反映
  5. 反映されたリアルDOMをブラウザがレンダリング

ということで最終的にリアルDOMを操作するのですが、通常、リアルDOMを操作する場合はリアルDOMが変更されるたびにブラウザがHTMLを解析してレンダリングするのでコストが高いです。

仮想DOMを使うメリットはレンダリングコストを低くできることの他に、

  • UIとロジックを分離できる
  • 状態の管理を簡略化できる
  • UIとロジックを繋ぐ処理が簡単になる

です。

じゃあどう変わるのか見てみようじゃないの

  • リアルDOMを操作する場合
  • 仮想DOMをVue.jsを使って操作する場合

を見てみましょう。

リアルDOMを操作する場合

こちらを参考にさせていただきました。

<div id="app">
  <p id="counter">0</p>
  <button type="button" id="increment">+1</button>    
</div>

<script>
const state = { count: 0 };
const btn = document.getElementById('increment');
btn.addEventListener('click', () => {
  const counter = document.getElementById('counter');
  counter.innerText = ++state.count;
})
</script>

リンク先にもありますが、このコードを見ると

  1. stateというオブジェクトで現在のcountを管理しよう
  2. ボタンをクリックしたらインクリメント処理を行おう
  3. state.countをインクリメントしよう
  4. state.countを表示するために表示する要素(p#counter)を取得しよう
  5. 取得した要素の文字をstate.countで更新しよう

と考えると思います。まぁこれでもいいんですけど、

  • いちいち要素をJavascriptで取得してるからUIとロジックが混在
  • HTMLにもJavascriptにも状態の初期値が記載
  • UIとロジックを結びつけるためにわざわざリスナーを定義してる

っていうのがめんどくさいですね。

仮想DOMをVue.jsを使って操作する場合

こんな感じのコードになるかと思います。

<template>
  <div>
    <p>{{ count }}</p>
    <button v-on:click="increment">+1</button>
  </div>
</template>

<script>
new Vue({
  data: {
    count: 0
  },
  methods: {
    increment: function() {
      this.count += 1
    }
  }
})
</script>

ね?

  • JavascriptでHTMLの要素を取得しないからUIとロジックが分離
  • 状態の初期値はJavascript側で完結
  • リスナー代わりのv-onディレクティブがHTML側に記載されてるからUIとロジックを繋ぐ処理が簡略化

されているでしょう。これが仮想DOMを使うメリットです。

あれ?結果的にレンダリングコストは変わるん?

一応色々とベンチマークはあるようなのですが、フレームワークによって得意不得意がある模様です。

まとめ

以上で、仮想DOMの説明をしてみました。仮想DOMは確かにレンダリングコストを低減する画期的なものですが、やはり開発する上ではUIとロジックが分離されるという点も非常に強力で、生産性向上に寄与するものだと思います。

謝辞

今回の記事について、様々な記事にお世話になりました。
この場をお借りしてお礼申し上げます。