Vue.jsの仮想DOMのレンダリング処理はどのようにして動いているのか

このエントリーは、Vue.js #2 Advent Calendar 2018アドベントカレンダーの4日目の記事です。

3日目は 【2018年】Vueのテスト関連まとめ【非公式】 です。

5日目は @nekobato さんの 「VueとAtomic Design」ですね!


以前、「Vueの仮想DOMアルゴリズムはすごい」という文をどこかで読んだ記憶があり、業務を通してAngular、Reactとやってきた私としてはずっと気になっていたのですが、「はて、どこで読んだんだったかな」と探してみても、見つけることが出来ませんでした。(もしかして何かの書籍で読んだんだったかな?)

そこで、どうせならばと自分なりに調べ、「Vue.jsの仮想DOMのレンダリング処理はどのようにして動いているのか」について、簡単ではありますが整理してみました。

TL;DR

  • JavaScriptにあまり強くない私が、「Vue.jsの仮想DOMのレンダリング処理はどのようにして動いているのか」と興味を持ったことから、調べてみることにした。
  • ADWAYSさんのテックブログ が非常に参考になった。
  • 完全にソースコードを読み進めロジックを理解する必要があり、それには時間が足りないため厳しい結果に終わった。
  • というわけで、このエントリー自体は本当にたいした内容にすることが出来なかったのでぶっちゃけ読まなくても大丈夫です。
  • ご清聴ありがとうございました!

仮想DOMのレンダリング処理は”どこで行っている”のか?

「Vue.jsの仮想DOMのレンダリング処理はどのようにして動いているのか」を知るためには、まず「Vue.jsの仮想DOMのレンダリング処理はどこで行われているのか」を、ソースコードレベルで知る必要がありました。

軽く検索してみると、ADWAYSさんのテックブログ(下記の記事)によくまとめられた記事があり、非常に参考になりました。(いきなり引用で申し訳ありません・・・。)

blog.engineer.adways.net

blog.engineer.adways.net

blog.engineer.adways.net

といいますか、「もうこの記事読めば良いのではないか?」とさえ思うほどでした・・・。

・・・せっかくなので整理を進めて行きます。

ADWAYSさんのブログを読むと

- Vue.jsの仮想DOMも、差分レンダリング
- 仮想DOMの差分レンダリングは、[src/core/vdom/patch.js](https://github.com/vuejs/vue/blob/dev/src/core/vdom/patch.js) にて行われている。
- 仮想DOMの差分レンダリングタイミングは、 [src/core/instance/lifecycle.js](https://github.com/vuejs/vue/blob/dev/src/core/instance/lifecycle.js) にて管理されている。
- Vue.jsの差分レンダリングは、初回レンダリング時と次回以降のレンダリング時で異なる。
-  `src/core/instance/lifecycle.js` に定義されている `lifecycleMixin function` にて、状態更新処理を行う関数の初期化( `_update` という関数 )を行っている。
- `_update関数` は、同じく `src/core/instance/lifecycle.js` に定義されている `mountComponent関数` から呼ばれている。
- `mountComponent関数` は初回レンダリング時に作用する
- 次回以降のレンダリング時は、Watcherが良い感じに監視を行う。
- コンポーネントが最終的に内部に保持する `VNode` というものが、仮想DOMツリー生成ロジックの理解に必要。
- 親と子のコンポーネントがあった場合、親コンポーネントは子コンポーネントをVNodeとして保持する。
- このVNodeを構築するのが仮想DOMを構築すると同義?
- 再レンダリング時は差分を見るため、やや処理が複雑になる。
- 再レンダリングは `patchVnode` という関数が行っている。
- 古いVNodeと新しいVNodeを見比べて、子Nodeの状態からみて子Nodeを更新する。
- 古いVNodeと新しいVNodeを見比べて、それぞれに違いがある場合は新しいVNodeの内容で更新する。
- 再帰的に実行していく。

というような事ががわかりました。

実際、ロジックを追うのが難しかった

さて、読んだだけで満足していてはおんぶに抱っこで、何も学習したことにはなりません。

そこで、当該ソースコードをちゃんと読みすすめてみたく。

とはいっても、 patch.jslifecycle.js は1〜2時間で追うようなロジックではありませんでした。(もちろん私個人のスキルレベルに起因する問題と、家族サービス時間とのトレードオフ🤗)

patch.js だとv2.5.17時点で800行くらいなので、もう少し時間が欲しい所。

とりあえずcloneしたので、ローカルのエディターでもうすこし時間をかけてロジックを追いかけたいと思います。

(ついでと言ってはナンですが)仮想DOMって結局ナニモノなのだろう?

そう思い、思い出したのがこちらの記事です。

自作フレームワークをつくって学ぶ 仮想DOM実践入門

そして、Vueに関した仮想DOMの記事では、少し古いかつ英語ですが、こちらの記事が良かったです。

What's The Deal With Vue's Virtual DOM?

軽く翻訳というか意訳というかなんというか紹介させていただきますと、

- DOMはHTMLを表すものだと考える傾向にあるんやけど、実際はブラウザがDOMを解釈するとツリー状のデータ構造なんや。
- ブラウザってのはDOMの状態を描画するんやけど、DOM構造はJavaScriptのAPIを使って更新して再描画することが出来るんや。
- DOMの再描画っちゅーのは、その変更箇所を見つけて更新をする必要があんや。まあ、小さいDOMツリー構造やったらそんなにえらいことやないんやな。せやけど、DOMのツリー構造が大きなるとまあまあ結構なコストになるんや。
- 実はな、JavaScriptのデータ構造でも同じ様にDOMを表すことができるんや。これをまあ「仮想DOM」と呼ぶことにすんねんな。
- このJavaScriptのデータを使うて、
- 「せやけど、なんで「仮想DOM」を使うんやー?」思うかもしれけんけどな、まあ簡単に言うと「 `.getElementById` 」みたいなのを使うんやないから、早いんやな。
- それからな、仮想DOMの嬉しいところはパフォーマンス向上だけやないんや。機能追加も楽になるんや。
- 仮想DOMをプログラマブルに作ったりな、JavaScriptやからサーバーサイド(Node.jsでのこと?)も出来たりな、コンポーネントベースの設計も出来るんや。

(間違っていたらご指摘いただきたく)

英語はあまり得意ではないのでこのくらいにしておきたいのですが、仮想DOMを使うことの基本的な利点を簡潔かつ丁寧に説明されていると思いました。

さいごに

Vue.jsを利用する上で仮想DOMの差分更新をする処理なんかは、少し乱暴な言い方かもしれませんがフレームワークの意義から考えると、実際はあまり意識しなくても良い気がします。

しかし、どのようにして仮想DOMが構築され、どの様にして(あるいはどのタイミングで)差分更新が行われるかをイメージレベルでも理解していると、今後何かの役に立つかもしれませんね。

それから、普段自分が利用している”モノ”がどう作られているかを知るのは、純粋に楽しいことです。


稚拙な文章かつ、とりとめの無い内容となってしまい、しかも結局自分の力では何も得られるものがありませんでしたが、以上になります。

私自身、まだまだ学習が足りないなと実感する日々です。

ですが、少しでもVue.jsとコミュニティーの発展に貢献できれば幸いです。

もしここまで読んでしまわれた方がいらっしゃいましたら、大変申し訳ございませんで誠にありがとうございましたした。