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

この記事は福岡若手[Sier_bc Advent Calendar 2019](https://qiita.com/advent-calendar/2019/fukuoka-sier_young_bc)の11日目の記事です。

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

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

を対象にしています。

# そもそもDOMって何よ？
「Document Object Model」の略ですが、[Wikipedia先生の説明](https://ja.wikipedia.org/wiki/Document_Object_Model)では

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

要するに、
__HTMLを構築する木構造データのことだよ！プログラミング言語で操作できるよ！__
ってことです。ここで注意しなければならないのは、[MDNの説明](https://developer.mozilla.org/ja/docs/Web/API/Document_Object_Model)にも

>(前略)ふつうは 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を操作する場合
[こちら](https://kuroeveryday.blogspot.com/2018/11/how-to-create-virtual-dom-framework.html)を参考にさせていただきました。

```html
<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を使って操作する場合
こんな感じのコードになるかと思います。

```html
<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を使うメリットです。

# あれ？結果的にレンダリングコストは変わるん？
一応色々とベンチマークはあるようなのですが、フレームワークによって得意不得意がある模様です。

- [Results for js web frameworks benchmark – round 8](https://stefankrause.net/js-frameworks-benchmark8/table.html)
- [Vue・React・Angularのパフォーマンス比較検証](https://ics.media/entry/190731/)

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

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

- [なぜ仮想DOMという概念が俺達の魂を震えさせるのか](https://qiita.com/mizchi/items/4d25bc26def1719d52e6)
- [自作フレームワークをつくって学ぶ 仮想DOM実践入門](https://kuroeveryday.blogspot.com/2018/11/how-to-create-virtual-dom-framework.html)
- [Vue・React・Angularのパフォーマンス比較検証](https://ics.media/entry/190731/)