トップ/記事一覧

client-only タグと Hydration のインプット

📆2022/10/07(最終更新日:2022/10/13)🔖 NuxtJS

会社で運用しているプロジェクトで、Nuxt.js のクライアントサイドで v-html で展開する部分でエラーを吐いていた。CMS から HTML タグが返ってきてそれを画面上に描画するという仕組みになっているところだった。

エラーの原因を調査をしていたのがきっかけで、client-only と Hydration の仕組みを今更ながらインプットしたので、それの覚え書きをする。Nuxt の基礎的な部分のはずだが、今までハマったことがなくきちんと理解できていない部分だったので、良い勉強の機会になった。

(理解誤ってるところあったら教えて下さい🙇)

🐈

今回出ていたエラーは以下。内容は少し不親切に感じるが、プロダクションコード内ではなく、Nuxt 側のエラーであることはファイル名見るとだいたい分かる。

plain text

n.setAttribute is not a function

DOM 由来っぽいオーラを感じたので、template 内の DOM を少しづつ消して、原因となっている箇所を地道に特定していった。

Hydration について

そもそも Hydration の定義はこんな感じらしい(知らなかった)

静的な HTML を引き継いで、クライアントサイドのデータ変更に対応でき仮想 DOM に変換するクライアントサイドのプロセス

https://v3.ja.vuejs.org/guide/ssr/hydration.html

そして Hydration の処理内で、仮想 DOM とブラウザの HTML のギャップを確認するプロセスがあるっぽい。ここで仮想 DOM とブラウザ HTML の構造にギャップがある場合、Hydration 時にエラーを吐くらしい。今回はこれで引っかかっていた。

僕たちのプロダクトは、SEO 的な評価の担保(Google にきちんとインデックスさせる必要)や・シェアしたときにきちんと OGP が表示されるというのが要件にあるので、静的に HTML を生成してやる必要がある。静的な HTML の生成には CMS からデータを取得する部分があり、記事の本文やタイトルをビルド時に取得している。

今回エラーが起きていた箇所の調査を進めると、CMS からデータを取得して v-html でレンダリング *1 する部分にギャップを発生する要因があったということが分かった。具体で言うと、テーブルタグの構造が誤っていた。table タグ直下に p タグが来るみたいな構造になってしまっており、静的な HTML の生成が、DOM 構造的に NG なものとなっていた。

ブラウザの仕様について

少し脱線するが、ブラウザ側の機能で、誤った構造の HTML を自動で補正してくれる機能があるらしい。

DOM 構造的に NG なものがあるとなぜ仮想 DOM とブラウザ HTML の間にギャップが発生し、エラーが出てしまうのかと言うと、ブラウザ側が補正した HTML と、静的生成された誤った構造の HTML にギャップがあったからというわけだ。

ブラウザ HTML 側では誤った構造になっていた table タグは無視されて、静的な HTML の方(仮想 DOM)には table タグが存在するみたいな状態になっていた。どおりでエラーを吐くわけだ。

client-only タグについて

囲んだ箇所は静的な HTML として吐き出されなくなるという仕様。yarn generate 時に dist 配下に静的なページが生成され、HTML が生まれるが、client-only タグで囲んだ箇所はタグが生成されない。

この部分に関してはそもそもの HTML がないので Hydration の差分チェック対象にならない。今回は、該当箇所に client-only を付けるという対応をした。

client-only タグの仕様を調査

今回どうしたか

データ入稿時に誤ったタグを入れないように注意するというのはそれはそうだが、セーフティーネット的に client-only タグを入れておくことにした。client-only を入れた箇所は SEO 的に弱くなってしまう懸念が少しあるが、運用ミスでエラーが出るよりはマシという判断をした。

🐈

*1 「CMS から HTML 返すなよ」というのはそれはそうなのだけれども、諸々の社内の事情があるということを察してもらえればと思う。