Mismatching childNodes vs. VNodes の原因と対処

Published: 2022/6/21


問題

Mismatching childNodes vs. VNodes:
(DOM Node の一覧と VNode の一覧)
[Vue warn]: The client-side rendered virtual DOM tree is not matching server-rendered content. This is likely caused by incorrect HTML markup, for example nesting block-level elements inside <p>, or missing <tbody>. Bailing hydration and performing full client-side render.

Nuxt など、 SSR を行う vue アプリケーションにて開発を行っていると、上記のエラーが発生することがある。 これは、なんだかページのロード時の挙動が若干おかしいかったり、ぎこちなかったりで、調査のためにコンソールを開いてみると、上記エラーが発生していて気付く、といった場合が多い。

原因

SSR を Vue で行う場合、サーバーで生成された html を元にして構成された DOM に対して、クライアント側で SPA の render の結果との突合を行う。 その際、 HTML から生成された DOM のオブジェクトと、 Vue を render しようとして得られる Virtual DOM の間に構造的な差分がある場合、何かおかしなことが発生したとして、クライアント側で、 render を一からやりなおし、 DOM を render の結果から再生成して、それでもってマウントポイントに代入する、といったようなことを行う。

DOM の総入れ替えのようなことを行うことになるので、これは効率が悪いし、最初の HTML を読み込んだ結果と、SPA として mount が完了した状態とで、 DOM の構造が変わってしまうので、 FOUC 的なことが発生し、 UX も低下する。

対処方法

HTML から読み取られた DOM と SPA としての render の結果の不一致が問題であるので、何故それが発生するかを突き止めて、発生しないようにすれば良い。 大体の場合において、 HTML として不適格な VDom を render した結果、それをブラウザが解釈した際に、良い感じに補完等を行って、 VNode 時点の構造と、ブラウザが用意する DOM の構造との間で、不整合が起きるのが原因。

自分が調べた限りだと、以下のケースがある模様。

  • エラーメッセージ自体にあるように、<p> タグの中でブロックエレメントが表われていたり、 <table><tbody> がないケース
    • ブラウザが補完する。
  • コメントの隣にテキストがあるケース
    • これは、 HTML の構造というより、 vue の SSR と hydration 上のバグな気はする
  • <a> タグが二重化しているケース
    • ブラウザが、勝手に <a> タグを終わらせたりして、二重化を防いだりする。

Tags: vuenuxt

関連記事

About

エンジニアです。 仕事では Xincere Residence を作っています。 このサイトは個人のブログであり、所属団体の意見等とは関係がありません。

Tags